Здравствуйте, gandjustas, Вы писали:
T>>Я видимо не до конца понимаю, что имеется в виду. T>>Наследование, интерфейсы, методы, атрибуты — для меня это удобства, от которых я бы очень не хотел отказываться. G>Не отказывайтесь. Но базовая модель для данных — ER (потому что БД так работает) и объектная модель должна при желании надстраиваться над ER, причем все "прелести" как ленивая загрузка должны быть опциональны и по-умолчанию выключены.
Может поподробней описать, что имеется в виду? В реляционной модели не поддерживаются такие понятия, как наследование, область видимости для данных, методы, меняющие состояние entity. Если все это привнести в нее, то это будет не реляционная, а вполне себе объектная модель.
T>>>>Как EF, так и Linq2Sql поддерживают ленивую загрузку. G>>>Неправда, EF не поддерживает ленивую зугрузку. Да и вообще ленивая загрузка — большое зло, в программе надо видеть где происходит обращение к БД. T>>Согласен, погорячился, но Linq2Sql поддержиает G>В Linq2SQL гораздо сложнее отключить ленивую загрузкую.
Убираешь все relation-ы и ленивые поля и получаешь то, к чему ты так стремишься.
T>>Не забывай добавлять "в простых (а потому нежизненных) сценарих". Все равно нужно будет делать валидацию/проверку прав/аудит, тут-то объект у вас в память и заползет. G>Да ну? Как раз security можно создать дополнительными фильтрами выборки. В моих проектах так и делается, при выборке в память не тянутся объекты для которых доступа нету. G>При записи все равно приходится вытягивать нужный объект(с теми же secutiry фильтрами). При наличии операторов update\delete теже фильтры перекочевали бы в соответствующие запросы и не было бы необходимости тянуть целый объект.
Тогда бы написание этих запросов превратилось в сущий ад. В лес такое счастье.
T>>>>Де-факто, прежде чем изменить что-то в базе понадобится еще сделать валидацию, поправить информацию о дате последнего изменения, изменить зависимые данные и так далее и тому подобное. G>>>То что касается целостности данных должно находится в БД и все проверки и изменения должны осуществляться самой БД. T>>Ну приплыли. G>Ну не знаю как у вас, а у меня так работает. И отлично работает, по поводу lastModified вообще никто не парится.
Аудит — это не только lastModified. Есть еще аудит удаленных объектов, хранение истории изменений и так далее. Тоже все это в запрос пихать?
G>>>То что касается бизнес-логики вполне может быть условием в самом запросе T>>До тех пор, пока бизнес-логика ограничивается простыми правилами. G>Сложные правила также могут быть выражены в запросах, которые выполняются в SQL, а при помощи Linq записаны в коде приложения.
Теоретически они могут быть туда записаны. А на практике правила зачастую настолько сложны, что что их даже в императивном варианте записать непросто.
G>Но пока все изменения объектов производятся в памяти, то такой подход неэффективен. Вам придется записать все изменения, запустить запросы, осуществляющие валидацию модели, при неуспешном результате откатить транзакцию. Транзакцию и код проверки для этого придется запускать вручную.
Транзакцию и так придется запускать вручную. Операции затрагивающие одну единственную таблицу встречаются чрезвычайно редко.
G>Если все изменения данных выполняются реляционными операторами и бизнес-правила выражены запросами, то все запросы можно выполнить в одной транзакции БД (при этом практически не тянуть в память объекты), при этом вопросами целостности и конкуретного доступа к данным будет заниматься БД.
А почему в случае UoW она не может заняться вопросами целостности и конкурентного дотупа?
G>>>или проверяться до обращения к данным. T>>Где? В каком слое приложения? G>В PL — проверка введенных данных и в BLL — проверка агрументов и параметров окружения.
PL — это что? страница, форма. А если у BLL несколько пользователей? Например, UI и веб-сервис?
Здравствуйте, Tissot, Вы писали:
T>Здравствуйте, gandjustas, Вы писали:
T>>>Я видимо не до конца понимаю, что имеется в виду. T>>>Наследование, интерфейсы, методы, атрибуты — для меня это удобства, от которых я бы очень не хотел отказываться. G>>Не отказывайтесь. Но базовая модель для данных — ER (потому что БД так работает) и объектная модель должна при желании надстраиваться над ER, причем все "прелести" как ленивая загрузка должны быть опциональны и по-умолчанию выключены.
T>Может поподробней описать, что имеется в виду? В реляционной модели не поддерживаются такие понятия, как наследование, область видимости для данных, методы, меняющие состояние entity. Если все это привнести в нее, то это будет не реляционная, а вполне себе объектная модель.
Подсказка: EDM в EF — практически ER описание, пригодное для использования в программе на .NET. Вполне поддерживается и наследование, и область видимости. Методы, меняющие состояние, вы вполне можете сделать через extensionы.
Вы вообще путаете суть и форму. Классы и объекты — это форма, суть в том что в случае EDM это описание сильно соответствует ER описанию.
С помощью Linq вы можете строить выборки данных с помощью синтаксиса классов и объектов не имея фактически объектов (их состояния) в памяти.
T>>>Не забывай добавлять "в простых (а потому нежизненных) сценарих". Все равно нужно будет делать валидацию/проверку прав/аудит, тут-то объект у вас в память и заползет. G>>Да ну? Как раз security можно создать дополнительными фильтрами выборки. В моих проектах так и делается, при выборке в память не тянутся объекты для которых доступа нету. G>>При записи все равно приходится вытягивать нужный объект(с теми же secutiry фильтрами). При наличии операторов update\delete теже фильтры перекочевали бы в соответствующие запросы и не было бы необходимости тянуть целый объект.
T>Тогда бы написание этих запросов превратилось в сущий ад. В лес такое счастье.
С появлением Linq написание выборок гораздо упростилось, сейчас многих уже и не заставишь вложенные циклы писать вместо использования Linq. С чего вы взяли что при расширении Linq операторами insert, update, delete написание запросов превратится в ад?
Пример (гипотетический)
var someQuery = from el in collection where ... select c;//такое уже есть
delete from c in collection where ... //Сложно?
update c in collection where ... set c.Prop1 = val1, c.Prop2 = val2 //Тоже не очень мудрено
insert into collection2 (someQuery) //любое выражение типа IEnumerable<T> в скобках
Где здесь ад? Большая часть уже сделана в Linq запросах, добавить осталось совсем немного.
T>>>>>Де-факто, прежде чем изменить что-то в базе понадобится еще сделать валидацию, поправить информацию о дате последнего изменения, изменить зависимые данные и так далее и тому подобное. G>>>>То что касается целостности данных должно находится в БД и все проверки и изменения должны осуществляться самой БД. T>>>Ну приплыли. G>>Ну не знаю как у вас, а у меня так работает. И отлично работает, по поводу lastModified вообще никто не парится.
T>Аудит — это не только lastModified.
Бывает.
T>Есть еще аудит удаленных объектов,
View + instead of delete триггер
T>хранение истории изменений и так далее.
триггеры
T>Тоже все это в запрос пихать?
Не стоит. Этим должна БД заниматься, причем прозрачно для пользователя.
G>>>>То что касается бизнес-логики вполне может быть условием в самом запросе T>>>До тех пор, пока бизнес-логика ограничивается простыми правилами. G>>Сложные правила также могут быть выражены в запросах, которые выполняются в SQL, а при помощи Linq записаны в коде приложения.
T>Теоретически они могут быть туда записаны. А на практике правила зачастую настолько сложны, что что их даже в императивном варианте записать непросто.
В таком случае надо разбивать на более простые... ну как обычно.
G>>Но пока все изменения объектов производятся в памяти, то такой подход неэффективен. Вам придется записать все изменения, запустить запросы, осуществляющие валидацию модели, при неуспешном результате откатить транзакцию. Транзакцию и код проверки для этого придется запускать вручную.
T>Транзакцию и так придется запускать вручную. Операции затрагивающие одну единственную таблицу встречаются чрезвычайно редко.
Также как и вручную вызыыать SaveChanges. Только немного по-другому рабоать будет. В случае работы с объектами вы сначала получаете их из базы, производите операции (при этом пройти может бесконечно много времени), потом пишете в базу изменения.
В случае работы с запросами вы формируете запросы: создаете выборки без материализации записей, подставляете эти выборки в выражения insert\update\delete, в конце батча добавляете запросы, обеспечивающие валидацию, а потом запускаете батч на исполнение. О всем остальном заботится БД.
G>>Если все изменения данных выполняются реляционными операторами и бизнес-правила выражены запросами, то все запросы можно выполнить в одной транзакции БД (при этом практически не тянуть в память объекты), при этом вопросами целостности и конкуретного доступа к данным будет заниматься БД. T>А почему в случае UoW она не может заняться вопросами целостности и конкурентного дотупа?
Синклер привел пример по этому поводу, прочитайте его повнимательнее.
G>>>>или проверяться до обращения к данным. T>>>Где? В каком слое приложения? G>>В PL — проверка введенных данных и в BLL — проверка агрументов и параметров окружения.
T>PL — это что? страница, форма. А если у BLL несколько пользователей? Например, UI и веб-сервис?
Вы не умеете делать валидацию введенных данных?
G>>Не отказывайтесь. Но базовая модель для данных — ER (потому что БД так работает) и объектная модель должна при желании надстраиваться над ER, причем все "прелести" как ленивая загрузка должны быть опциональны и по-умолчанию выключены.
T>Может поподробней описать, что имеется в виду? В реляционной модели не поддерживаются такие понятия, как наследование, область видимости для данных, методы, меняющие состояние entity. Если все это привнести в нее, то это будет не реляционная, а вполне себе объектная модель.
А зачем?
SOA тоже хреново поддерживает ООП и интерфейсы, а в версии MS не поддерживает СПЕЦИАЛЬНО,
однако это не мешает ей быть лидирующей технологией для определённого круга задач.
Чем ER хуже?
Или у тебя есть супермолоток?
Здравствуйте, gandjustas, Вы писали:
T>>Может поподробней описать, что имеется в виду? В реляционной модели не поддерживаются такие понятия, как наследование, область видимости для данных, методы, меняющие состояние entity. Если все это привнести в нее, то это будет не реляционная, а вполне себе объектная модель.
G>Подсказка: EDM в EF — практически ER описание, пригодное для использования в программе на .NET. Вполне поддерживается и наследование, и область видимости. Методы, меняющие состояние, вы вполне можете сделать через extensionы. G>Вы вообще путаете суть и форму. Классы и объекты — это форма, суть в том что в случае EDM это описание сильно соответствует ER описанию.
Еще раз, в ER модели нет понятий наследования, области видимости, методов и т.д. Вы очень неудачно пользуетесь терминами, которые имеют вполне определенное значение. Посмотрите в википедии.
G>>>При записи все равно приходится вытягивать нужный объект(с теми же secutiry фильтрами). При наличии операторов update\delete теже фильтры перекочевали бы в соответствующие запросы и не было бы необходимости тянуть целый объект.
T>>Тогда бы написание этих запросов превратилось в сущий ад. В лес такое счастье. G>С появлением Linq написание выборок гораздо упростилось, сейчас многих уже и не заставишь вложенные циклы писать вместо использования Linq. С чего вы взяли что при расширении Linq операторами insert, update, delete написание запросов превратится в ад? G>Пример (гипотетический) G>
G>var someQuery = from el in collection where ... select c;//такое уже есть
G>delete from c in collection where ... //Сложно?
G>update c in collection where ... set c.Prop1 = val1, c.Prop2 = val2 //Тоже не очень мудрено
G>insert into collection2 (someQuery) //любое выражение типа IEnumerable<T> в скобках
G>
G>Где здесь ад? Большая часть уже сделана в Linq запросах, добавить осталось совсем немного.
Ничего сложного, но где здесь security, валидация, аудит?
T>>>>Ну приплыли. G>>>Ну не знаю как у вас, а у меня так работает. И отлично работает, по поводу lastModified вообще никто не парится.
T>>Аудит — это не только lastModified. G>Бывает.
T>>Есть еще аудит удаленных объектов, G>View + instead of delete триггер
T>>хранение истории изменений и так далее. G>триггеры
T>>Тоже все это в запрос пихать? G>Не стоит. Этим должна БД заниматься, причем прозрачно для пользователя.
Как понять в триггере, что запись изменил не sa, а "Васиий Петров"?
Что будем делать если нужны осмысленные записи в логе?
Как все это потом тестировать?
T>>Теоретически они могут быть туда записаны. А на практике правила зачастую настолько сложны, что что их даже в императивном варианте записать непросто. G>В таком случае надо разбивать на более простые... ну как обычно.
Твоими бы устами ...
G>>>Но пока все изменения объектов производятся в памяти, то такой подход неэффективен. Вам придется записать все изменения, запустить запросы, осуществляющие валидацию модели, при неуспешном результате откатить транзакцию. Транзакцию и код проверки для этого придется запускать вручную.
T>>Транзакцию и так придется запускать вручную. Операции затрагивающие одну единственную таблицу встречаются чрезвычайно редко. G>Также как и вручную вызыыать SaveChanges. Только немного по-другому рабоать будет. В случае работы с объектами вы сначала получаете их из базы, производите операции (при этом пройти может бесконечно много времени), потом пишете в базу изменения.
Тогда я не понимаю, зачем вы писали "все запросы можно выполнить в одной транзакции БД"?
Зачем это подчеркивать, если и так в обоих случаях все будет работать аналогично?
G>В случае работы с запросами вы формируете запросы: создаете выборки без материализации записей, подставляете эти выборки в выражения insert\update\delete, в конце батча добавляете запросы, обеспечивающие валидацию, а потом запускаете батч на исполнение. О всем остальном заботится БД.
Хорошо, хотя бы на уровне псевдокода опиши как это будет выглядеть без материализации данных из базы для следующего сценария:
Если кастомер из категории VIP, то для него должно быть разрешено резервировать заказ на складе без проверок остатков.
G>>>Если все изменения данных выполняются реляционными операторами и бизнес-правила выражены запросами, то все запросы можно выполнить в одной транзакции БД (при этом практически не тянуть в память объекты), при этом вопросами целостности и конкуретного доступа к данным будет заниматься БД. T>>А почему в случае UoW она не может заняться вопросами целостности и конкурентного дотупа? G>Синклер привел пример по этому поводу, прочитайте его повнимательнее.
Спасибо, я прочитал. И даже ответил.
G>>>>>или проверяться до обращения к данным. T>>>>Где? В каком слое приложения? G>>>В PL — проверка введенных данных и в BLL — проверка агрументов и параметров окружения.
T>>PL — это что? страница, форма. А если у BLL несколько пользователей? Например, UI и веб-сервис? G>Вы не умеете делать валидацию введенных данных?
А речь не обо мне, а о вашей архитектуре. Где будет происходить валидация.
Предвидя ваш ответ, чуть усложним постановку: модуль A используется модулем B, модуль B создает новый объект в модуле A. Кто должен будет проверить валидность созданного объекта?
Здравствуйте, Tissot, Вы писали:
T>Здравствуйте, gandjustas, Вы писали:
T>>>Может поподробней описать, что имеется в виду? В реляционной модели не поддерживаются такие понятия, как наследование, область видимости для данных, методы, меняющие состояние entity. Если все это привнести в нее, то это будет не реляционная, а вполне себе объектная модель.
G>>Подсказка: EDM в EF — практически ER описание, пригодное для использования в программе на .NET. Вполне поддерживается и наследование, и область видимости. Методы, меняющие состояние, вы вполне можете сделать через extensionы. G>>Вы вообще путаете суть и форму. Классы и объекты — это форма, суть в том что в случае EDM это описание сильно соответствует ER описанию.
T>Еще раз, в ER модели нет понятий наследования, области видимости, методов и т.д. Вы очень неудачно пользуетесь терминами, которые имеют вполне определенное значение. Посмотрите в википедии.
Еще раз. Не путайте суть и форму. Форма будет одинаковая: классы, объекты, интерфесы, наследование. При ООП подходе у вас будет императивный код работы с объектами. При ER — декларативный код работы с сущностями.
G>>>>При записи все равно приходится вытягивать нужный объект(с теми же secutiry фильтрами). При наличии операторов update\delete теже фильтры перекочевали бы в соответствующие запросы и не было бы необходимости тянуть целый объект.
T>>>Тогда бы написание этих запросов превратилось в сущий ад. В лес такое счастье. G>>С появлением Linq написание выборок гораздо упростилось, сейчас многих уже и не заставишь вложенные циклы писать вместо использования Linq. С чего вы взяли что при расширении Linq операторами insert, update, delete написание запросов превратится в ад? G>>Пример (гипотетический) G>>
G>>var someQuery = from el in collection where ... select c;//такое уже есть
G>>delete from c in collection where ... //Сложно?
G>>update c in collection where ... set c.Prop1 = val1, c.Prop2 = val2 //Тоже не очень мудрено
G>>insert into collection2 (someQuery) //любое выражение типа IEnumerable<T> в скобках
G>>
G>>Где здесь ад? Большая часть уже сделана в Linq запросах, добавить осталось совсем немного.
T>Ничего сложного, но где здесь security, валидация, аудит?
Дополнительное выражение в where, или дополнительный запрос.
Или вы считаете что нельзя с помощью запросов выразить большенство операций?
T>Как понять в триггере, что запись изменил не sa, а "Васиий Петров"? T>Что будем делать если нужны осмысленные записи в логе? T>Как все это потом тестировать?
Для осмысленных записей в логе в БД есть таблица с пользователями.
Тестировать — спокойно. Создаете пустую базу, выполняете операцию, сверяете запись в логе с эталонной.
G>>>>Но пока все изменения объектов производятся в памяти, то такой подход неэффективен. Вам придется записать все изменения, запустить запросы, осуществляющие валидацию модели, при неуспешном результате откатить транзакцию. Транзакцию и код проверки для этого придется запускать вручную.
T>>>Транзакцию и так придется запускать вручную. Операции затрагивающие одну единственную таблицу встречаются чрезвычайно редко. G>>Также как и вручную вызыыать SaveChanges. Только немного по-другому рабоать будет. В случае работы с объектами вы сначала получаете их из базы, производите операции (при этом пройти может бесконечно много времени), потом пишете в базу изменения.
T>Тогда я не понимаю, зачем вы писали "все запросы можно выполнить в одной транзакции БД"? T>Зачем это подчеркивать, если и так в обоих случаях все будет работать аналогично?
Вы не поняли. Действительно можно будет выполнить ВСЕ запросы в одной транзакции. При использовании ОРМ придется сделать выборку в одной транзакции, а потом коммит в другой. Можно явно объявить TransactionScope, но тогда её придется явно завершить после вызова SaveChanges.
G>>В случае работы с запросами вы формируете запросы: создаете выборки без материализации записей, подставляете эти выборки в выражения insert\update\delete, в конце батча добавляете запросы, обеспечивающие валидацию, а потом запускаете батч на исполнение. О всем остальном заботится БД. T>Хорошо, хотя бы на уровне псевдокода опиши как это будет выглядеть без материализации данных из базы для следующего сценария: T>Если кастомер из категории VIP, то для него должно быть разрешено резервировать заказ на складе без проверок остатков.
//Предположим что здесь запрос, выполняющий резервирование
//Выборка кастомера, материализация не требуетсяvar cust = from c in context.Customers
where c.Id == customerId
where c.Category.Name == "VIP"select c;
//Теперь валидационный запрос, выполняется в тойже транзакции что и предыдущиеfrom ..... //Выборка зарезервированных позицийwhere (условие остатки < 0) && !cust.Any()
select context.ThrowError() //context.ThrowError - функция в БД, которая кидает ошибку и откатывает транзакцию.
При материализации последнего запроса вылетит ошибка и транзакция будет откачена если остатки ушли в минус, а кастомер не VIP.
G>>>>>>или проверяться до обращения к данным. T>>>>>Где? В каком слое приложения? G>>>>В PL — проверка введенных данных и в BLL — проверка агрументов и параметров окружения.
T>>>PL — это что? страница, форма. А если у BLL несколько пользователей? Например, UI и веб-сервис? G>>Вы не умеете делать валидацию введенных данных?
T>А речь не обо мне, а о вашей архитектуре. Где будет происходить валидация. T>Предвидя ваш ответ, чуть усложним постановку: модуль A используется модулем B, модуль B создает новый объект в модуле A. Кто должен будет проверить валидность созданного объекта?
Ниче не понял.. Циклические зависимости или как? По-русски опишите чего надо.
Я обычно валидацию на всех уровнях делаю: проверки значений полей ввода в интерфейсе, проверки параметром методов в BLL, валидация модели, констрейнты в БД.
Здравствуйте, gandjustas, Вы писали:
G>>>Вы вообще путаете суть и форму. Классы и объекты — это форма, суть в том что в случае EDM это описание сильно соответствует ER описанию.
T>>Еще раз, в ER модели нет понятий наследования, области видимости, методов и т.д. Вы очень неудачно пользуетесь терминами, которые имеют вполне определенное значение. Посмотрите в википедии.
G>Еще раз. Не путайте суть и форму. Форма будет одинаковая: классы, объекты, интерфесы, наследование. При ООП подходе у вас будет императивный код работы с объектами. При ER — декларативный код работы с сущностями.
Ок. Я понял, под er вы подразумеваете не er-модель. Проехали.
T>>Ничего сложного, но где здесь security, валидация, аудит? G>Дополнительное выражение в where, или дополнительный запрос. G>Или вы считаете что нельзя с помощью запросов выразить большенство операций?
Можно. Но мне не известо ничего о том как бороться со сложностью в запросах. А они при предлагаемых методах будут сложными.
T>>Как понять в триггере, что запись изменил не sa, а "Васиий Петров"? T>>Что будем делать если нужны осмысленные записи в логе? T>>Как все это потом тестировать? G>Для осмысленных записей в логе в БД есть таблица с пользователями.
То есть каждому пользователю давать свой логин в базе? Ты в курсе, что так делать не рекомендуют?
G>Тестировать — спокойно. Создаете пустую базу, выполняете операцию, сверяете запись в логе с эталонной.
Такие тесты работать будут часами. Главное требование к юнит-тесту — это то, что он работает оч. быстро. Станет работать медленно, никто их запускать не будет. На себе проверено.
G>>>>>Но пока все изменения объектов производятся в памяти, то такой подход неэффективен. Вам придется записать все изменения, запустить запросы, осуществляющие валидацию модели, при неуспешном результате откатить транзакцию. Транзакцию и код проверки для этого придется запускать вручную.
T>>>>Транзакцию и так придется запускать вручную. Операции затрагивающие одну единственную таблицу встречаются чрезвычайно редко. G>>>Также как и вручную вызыыать SaveChanges. Только немного по-другому рабоать будет. В случае работы с объектами вы сначала получаете их из базы, производите операции (при этом пройти может бесконечно много времени), потом пишете в базу изменения.
T>>Тогда я не понимаю, зачем вы писали "все запросы можно выполнить в одной транзакции БД"? T>>Зачем это подчеркивать, если и так в обоих случаях все будет работать аналогично? G>Вы не поняли. Действительно можно будет выполнить ВСЕ запросы в одной транзакции. При использовании ОРМ придется сделать выборку в одной транзакции, а потом коммит в другой.
Это еще почему?
G>Можно явно объявить TransactionScope, но тогда её придется явно завершить после вызова SaveChanges.
Это сложно?
G>>>В случае работы с запросами вы формируете запросы: создаете выборки без материализации записей, подставляете эти выборки в выражения insert\update\delete, в конце батча добавляете запросы, обеспечивающие валидацию, а потом запускаете батч на исполнение. О всем остальном заботится БД. T>>Хорошо, хотя бы на уровне псевдокода опиши как это будет выглядеть без материализации данных из базы для следующего сценария: T>>Если кастомер из категории VIP, то для него должно быть разрешено резервировать заказ на складе без проверок остатков.
G>
G>//Предположим что здесь запрос, выполняющий резервирование
G>//Выборка кастомера, материализация не требуется
G>var cust = from c in context.Customers
G> where c.Id == customerId
G> where c.Category.Name == "VIP"
G> select c;
G>//Теперь валидационный запрос, выполняется в тойже транзакции что и предыдущие
G>from ..... //Выборка зарезервированных позиций
G>where (условие остатки < 0) && !cust.Any()
G>select context.ThrowError() //context.ThrowError - функция в БД, которая кидает ошибку и откатывает транзакцию.
G>
G>При материализации последнего запроса вылетит ошибка и транзакция будет откачена если остатки ушли в минус, а кастомер не VIP.
То есть сначала мы пишем в базу, а потом валидируем? Оригинально
Это точно ты говорил, что материализация плохо отражается на перформансе?
T>>>>PL — это что? страница, форма. А если у BLL несколько пользователей? Например, UI и веб-сервис? G>>>Вы не умеете делать валидацию введенных данных?
T>>А речь не обо мне, а о вашей архитектуре. Где будет происходить валидация. T>>Предвидя ваш ответ, чуть усложним постановку: модуль A используется модулем B, модуль B создает новый объект в модуле A. Кто должен будет проверить валидность созданного объекта? G>Ниче не понял.. Циклические зависимости или как? По-русски опишите чего надо.
Пусть система состоит из 2-х подсистем: работа с кастомерами, база знаний.
Есть кастомер. При регистрации его в системе должен создаться соответствующий раздел в базе знаний.
Где (кем) будет проверяться, что созданный раздел создан корректно?
G>Я обычно валидацию на всех уровнях делаю: проверки значений полей ввода в интерфейсе, проверки параметром методов в BLL, валидация модели, констрейнты в БД.
Два сообщения назад валидации модели еще не было. Прогресс налицо!
P.S. На самом деле то, что вы описываете, до боли напоминает transaction script как он назван у Фаулера. Единственное отличие, которое я вижу — это его типизированность. В той же книжке описывается почему с ростом сложности приложения с transaction script-ом становится сложно жить. Рекомендую глянуть, если еще не смотрели.
Здравствуйте, VGn, Вы писали:
T>>Может поподробней описать, что имеется в виду? В реляционной модели не поддерживаются такие понятия, как наследование, область видимости для данных, методы, меняющие состояние entity. Если все это привнести в нее, то это будет не реляционная, а вполне себе объектная модель.
VGn>А зачем?
За надом (понимать буквально)
VGn>SOA тоже хреново поддерживает ООП и интерфейсы, а в версии MS не поддерживает СПЕЦИАЛЬНО, VGn>однако это не мешает ей быть лидирующей технологией для определённого круга задач. VGn>Чем ER хуже?
Причину отсутствия этого в SOA вполне можно понять. Зачем отказываться от этого в domain model я не понимаю.
Здравствуйте, Tissot, Вы писали:
T>Вот как раз в случае UoW удержание будет меньше.
Непонятно, за счёт чего. T>Рассмотри сценарий чуть посложнее, чем "обновить поле". Например в результате какой-то операции сначала нам понадобилось обновить у сustome-а поле A, потом провести какие-то достаточно длительные вычисления, потом обновить поле B.
Я такой сценарий представляю себе с большим трудом. T>В варианте с UoW на время операции будет наложена shared блокировка и только на время submit-а — более тяжелая exclusive.
А ничего, что в случае отката транзакции значение поля А, прочитанное кем-то во время "удержания shared блокировки", будет потеряно?
Если ничего, то можно разбить транзакцию на две, и не удерживать эксклюзивную блокировку во время тяжелых вычислений. А если такое нарушение целостности недопустимо, то UoW нужно срочно выбросить.
T>Очень интересно. Откуда же сервер узнает, что блокировку можно отпустить, что она более не понадобится?
S>>А теперь посмотрим, как бы это работало без Full Blown O/R mapper. S>>В старые добрые времена программист БД просто написал бы один запрос вида: S>>
S>>update StoreItem set Available = Available - OrderItem.Amount, Reserved = Reserved + OrderItem.Amount
S>>from StoreItem inner join OrderItem on StoreItem.ProductID = OrderItem.ProductId
S>>where OrderItem.OrderID = @OrderId and StoreItem.Available >= OrderItem.Amount;
S>>
S>>Сервер бы сам проследил за целостностью транзакции, не давая Кате модифицировать накладную в процессе резервирования. (Обеспечение запрета модифицировать накладную после резервирования потребовало бы чуть больше приседаний, но в чистом Оптимистично Блокированном мире это вообще невозможно без лишних телодвижений).
T>Во-первых, вы допустили ошибку в коде.
Где? T>Во-вторых, на самом деле все может быть сложнее: для каких-то категорий клиентов/товаров вполне может быть позволено заказывать в минуса, для каких-то категорий товаров должен гарантировано оставаться какой-то остаток на складах, если заказ крупный, то должно быть проверено, внесена ли предоплата и т.д. и т.п.. Я бы не хотел поддерживать код в котором все эти правила внесены в один update.
Именно облегчения поддержки такого кода мы и ждем от Linq. Потому, что вручную педалить все эти вещи — очень тяжело, и мы хотим контроль компилятора. Вот select стейтменты линк готовит прекрасно. Можно иметь произвольно сложную логику по динамическому формированию предикатов и всё еще получать статический контроль корректности.
T>Фишка в том, что это не код, а фикция.
Это смелое утверждение. T>Ты забыл добавить "если update — это единственная действие в бизнес-транзакции".
Ну, вообще-то подавляющее большинство "бизнес-операций" сводятся к очень ограниченному набору insert/update/delete. Особенно если проектировать приложение, держа это в уме.
Просто средства и фреймворки всегда навязывают своё мышление. "Реляционный" программист всегда старается свести задачу к манипуляции над реляциями. Как раз такого типа, как я показал. Вот ты привел примеры дополнительных бизнес-правил. "ООПшный" программист постарается избавиться от if-ов, которые проверяют "не нужно ли запасти ненулевой остаток для данной категории товара", и "нужна ли предоплата для данного размера заказа", путём выноса их в виртуальные методы некоторой иерархии классов. А реляционщик попробует свести всё это небольшому количеству массовых апдейтов.
То есть построит отдельную view "минимально допустимый остаток товара на складе" и приджойнит ее к запросу. И так далее.
Я еще раз подчеркиваю, что основное ограничение unit of work — в том, что он следит исключительно за изменениями, сделанными в транзакции. Он ничего не знает о том, какие причины привели к этим изменениям. Это означает, в частности, что для обеспечения элементарной транзакционной целостности, которая доступна в старинном SQL забесплатно, нужно делать специальные приседания.
T>А может зря оно себя таким уж прогрессивным считает?
Видишь ли, в чем дело. Это человечество разрабатывало ORM системы еще до первого коммита в репозиторий Hibernate и выхода Фаулеровского "P of EAA".
И был накоплен некоторый опыт взаимодействия с самыми разными архитектурами систем. Linq — это самое передовое достижение мысли в области работы с реляционными данными, но и у него есть недостатки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Tissot, Вы писали: T>Можно. Но мне не известо ничего о том как бороться со сложностью в запросах. А они при предлагаемых методах будут сложными.
Это ключевой момент в данной дискуссии.
Поясняю на пальцах: Linq и есть способ борьбы со сложностью в запросах.
Потому, что вместо кропотливого построения многоэтажных where, можно разбить процесс построения запроса на несколько частей:
S>Вот примерно так и борются со сложностью SQL запросов.
Ну в общем-то так и думал. Работал с таким — лучше чем ничего, но все равно недостаточно удобно. Если придумаете что-то более удобное — то честь вам и хвала.
Здравствуйте, Tissot, Вы писали:
T>Здравствуйте, gandjustas, Вы писали:
G>>>>Вы вообще путаете суть и форму. Классы и объекты — это форма, суть в том что в случае EDM это описание сильно соответствует ER описанию. T>>>Еще раз, в ER модели нет понятий наследования, области видимости, методов и т.д. Вы очень неудачно пользуетесь терминами, которые имеют вполне определенное значение. Посмотрите в википедии. G>>Еще раз. Не путайте суть и форму. Форма будет одинаковая: классы, объекты, интерфесы, наследование. При ООП подходе у вас будет императивный код работы с объектами. При ER — декларативный код работы с сущностями. T>Ок. Я понял, под er вы подразумеваете не er-модель. Проехали.
А что вы подразумеваете под ER моделью в программе?
T>>>Ничего сложного, но где здесь security, валидация, аудит? G>>Дополнительное выражение в where, или дополнительный запрос. G>>Или вы считаете что нельзя с помощью запросов выразить большенство операций? T>Можно. Но мне не известо ничего о том как бороться со сложностью в запросах. А они при предлагаемых методах будут сложными.
Известно. Linq в текущей реализации позоволяет последовательно строить выборку, накладывая дополнительные ограничения.
В коде выглядит как фильрация коллекции с разными условиями, только самой коллекции нету.
T>>>Как понять в триггере, что запись изменил не sa, а "Васиий Петров"? T>>>Что будем делать если нужны осмысленные записи в логе? T>>>Как все это потом тестировать? G>>Для осмысленных записей в логе в БД есть таблица с пользователями.
T>То есть каждому пользователю давать свой логин в базе? Ты в курсе, что так делать не рекомендуют?
Нет, есть таблица, хранящая логины и другие сведения о пользователях. В логах можно ссылкаться на эту таблицу.
G>>Тестировать — спокойно. Создаете пустую базу, выполняете операцию, сверяете запись в логе с эталонной. T>Такие тесты работать будут часами. Главное требование к юнит-тесту — это то, что он работает оч. быстро. Станет работать медленно, никто их запускать не будет. На себе проверено.
Вам не потребуется эти тесты запускать в совокупноти с unit-тестами приложения. Аудит в БД полностью ортогонален бизнес-логике в коде приложения.
G>>Вы не поняли. Действительно можно будет выполнить ВСЕ запросы в одной транзакции. При использовании ОРМ придется сделать выборку в одной транзакции, а потом коммит в другой. T>Это еще почему?
Потому что реализация такая. Linq2SQL и EF так работают.
G>>Можно явно объявить TransactionScope, но тогда её придется явно завершить после вызова SaveChanges. T>Это сложно?
Это сложнее чем ОДИН раз явно обявить транзакцию при использовании запросов.
G>>>>В случае работы с запросами вы формируете запросы: создаете выборки без материализации записей, подставляете эти выборки в выражения insert\update\delete, в конце батча добавляете запросы, обеспечивающие валидацию, а потом запускаете батч на исполнение. О всем остальном заботится БД. T>>>Хорошо, хотя бы на уровне псевдокода опиши как это будет выглядеть без материализации данных из базы для следующего сценария: T>>>Если кастомер из категории VIP, то для него должно быть разрешено резервировать заказ на складе без проверок остатков.
G>>
G>>//Предположим что здесь запрос, выполняющий резервирование
G>>//Выборка кастомера, материализация не требуется
G>>var cust = from c in context.Customers
G>> where c.Id == customerId
G>> where c.Category.Name == "VIP"
G>> select c;
G>>//Теперь валидационный запрос, выполняется в тойже транзакции что и предыдущие
G>>from ..... //Выборка зарезервированных позиций
G>>where (условие остатки < 0) && !cust.Any()
G>>select context.ThrowError() //context.ThrowError - функция в БД, которая кидает ошибку и откатывает транзакцию.
G>>
G>>При материализации последнего запроса вылетит ошибка и транзакция будет откачена если остатки ушли в минус, а кастомер не VIP.
T>То есть сначала мы пишем в базу, а потом валидируем? Оригинально
При таком подходе ВСЯ работа с данными происходит в БД, это нормально.
T>Это точно ты говорил, что материализация плохо отражается на перформансе?
Это так и есть.
T>Пусть система состоит из 2-х подсистем: работа с кастомерами, база знаний. T>Есть кастомер. При регистрации его в системе должен создаться соответствующий раздел в базе знаний. T>Где (кем) будет проверяться, что созданный раздел создан корректно?
Наверное тем кто создает раздел. К чему это все?
G>>Я обычно валидацию на всех уровнях делаю: проверки значений полей ввода в интерфейсе, проверки параметром методов в BLL, валидация модели, констрейнты в БД. T>Два сообщения назад валидации модели еще не было. Прогресс налицо!
Два сообщения назад я говорил о валидации, не касающейся данных в базе.
T>P.S. На самом деле то, что вы описываете, до боли напоминает transaction script как он назван у Фаулера. Единственное отличие, которое я вижу — это его типизированность. В той же книжке описывается почему с ростом сложности приложения с transaction script-ом становится сложно жить. Рекомендую глянуть, если еще не смотрели.
Я уже форуме "архитектура" про фаулера отписал. Повторюсь здесь.
Концептуальная разница между transaction script и dimain model состоит в том как вы хотите вызывать методы бизнес-логики
Так
Здравствуйте, Sinclair, Вы писали:
T>>Вот как раз в случае UoW удержание будет меньше. S>Непонятно, за счёт чего.
См. ниже.
T>>Рассмотри сценарий чуть посложнее, чем "обновить поле". Например в результате какой-то операции сначала нам понадобилось обновить у сustome-а поле A, потом провести какие-то достаточно длительные вычисления, потом обновить поле B. S>Я такой сценарий представляю себе с большим трудом.
А ты представь.
T>>В варианте с UoW на время операции будет наложена shared блокировка и только на время submit-а — более тяжелая exclusive. S>А ничего, что в случае отката транзакции значение поля А, прочитанное кем-то во время "удержания shared блокировки", будет потеряно?
Я не совсем интонацию понял. Для меня вопрос звучит столь же нелепо, как и спрашивать — "а ничего, что при откате транзакции изменения потеряются".
S>Если ничего, то можно разбить транзакцию на две, и не удерживать эксклюзивную блокировку во время тяжелых вычислений.
Нельзя, целостность будет потеряна.
S>А если такое нарушение целостности недопустимо, то UoW нужно срочно выбросить.
Не улавливаю ход рассуждения, распиши подробнее.
S>>>В старые добрые времена программист БД просто написал бы один запрос вида: S>>>
S>>>update StoreItem set Available = Available - OrderItem.Amount, Reserved = Reserved + OrderItem.Amount
S>>>from StoreItem inner join OrderItem on StoreItem.ProductID = OrderItem.ProductId
S>>>where OrderItem.OrderID = @OrderId and StoreItem.Available >= OrderItem.Amount;
S>>>
S>>>Сервер бы сам проследил за целостностью транзакции, не давая Кате модифицировать накладную в процессе резервирования. (Обеспечение запрета модифицировать накладную после резервирования потребовало бы чуть больше приседаний, но в чистом Оптимистично Блокированном мире это вообще невозможно без лишних телодвижений).
T>>Во-первых, вы допустили ошибку в коде. S>Где?
Задание на дом. Перечитай свое же сообщение еще раз.
T>>Во-вторых, на самом деле все может быть сложнее: для каких-то категорий клиентов/товаров вполне может быть позволено заказывать в минуса, для каких-то категорий товаров должен гарантировано оставаться какой-то остаток на складах, если заказ крупный, то должно быть проверено, внесена ли предоплата и т.д. и т.п.. Я бы не хотел поддерживать код в котором все эти правила внесены в один update. S>Именно облегчения поддержки такого кода мы и ждем от Linq. Потому, что вручную педалить все эти вещи — очень тяжело, и мы хотим контроль компилятора. Вот select стейтменты линк готовит прекрасно. Можно иметь произвольно сложную логику по динамическому формированию предикатов и всё еще получать статический контроль корректности.
Вручную педалить тяжело если вы остаетесь в рамках trabsaction script-а. Переходите на ооп подход и волосы становятся густыми и блестящими
T>>Фишка в том, что это не код, а фикция. S>Это смелое утверждение. T>>Ты забыл добавить "если update — это единственная действие в бизнес-транзакции". S>Ну, вообще-то подавляющее большинство "бизнес-операций" сводятся к очень ограниченному набору insert/update/delete. Особенно если проектировать приложение, держа это в уме. S>Просто средства и фреймворки всегда навязывают своё мышление. "Реляционный" программист всегда старается свести задачу к манипуляции над реляциями. Как раз такого типа, как я показал. Вот ты привел примеры дополнительных бизнес-правил. "ООПшный" программист постарается избавиться от if-ов, которые проверяют "не нужно ли запасти ненулевой остаток для данной категории товара", и "нужна ли предоплата для данного размера заказа", путём выноса их в виртуальные методы некоторой иерархии классов.
Совершенно верно.
S>А реляционщик попробует свести всё это небольшому количеству массовых апдейтов.
Пока у них это не очень хорошо получается. Те средства декомпозиции запросов, что есть в SQL-е и в базах данных (WITH, view, функции) просто смешны и не выдерживают никакой критики.
S>То есть построит отдельную view "минимально допустимый остаток товара на складе" и приджойнит ее к запросу. И так далее.
Как бороться со ложностью? Как это тестировать? и так далее.
ООП дает на эти вопросы более менее внятный ответ.
S>Я еще раз подчеркиваю, что основное ограничение unit of work — в том, что он следит исключительно за изменениями, сделанными в транзакции. Он ничего не знает о том, какие причины привели к этим изменениям. Это означает, в частности, что для обеспечения элементарной транзакционной целостности, которая доступна в старинном SQL забесплатно, нужно делать специальные приседания.
Не пойму, что тебе запрещает использовать танзакции-то? И полчишь "целостность, которая доступна в старинном SQL забесплатно".
T>>А может зря оно себя таким уж прогрессивным считает? S>Видишь ли, в чем дело. Это человечество разрабатывало ORM системы еще до первого коммита в репозиторий Hibernate и выхода Фаулеровского "P of EAA". S>И был накоплен некоторый опыт взаимодействия с самыми разными архитектурами систем. Linq — это самое передовое достижение мысли в области работы с реляционными данными, но и у него есть недостатки.
Все новое всегда кажется лучшим и более передовым. Linq-у не так много лет, чтобы успеть обкатать его в достаточной мере и почувствовать на собственной шкуре все недостатки, которых он конечно же не лишен.
Подходу же с linq-like insert-ами — и того меньше лет (0). Поэтому говорить о его достоинствах несколько рановато.
Здравствуйте, Tissot, Вы писали:
T>За надом (понимать буквально)
То есть, очень хочется, но объяснить не можешь? )
T> Зачем отказываться от этого в domain model я не понимаю.
Потому что там это не нужно и даже вредно.
Здравствуйте, Tissot, Вы писали:
T>Не стоит выдавать де-факто стандарт в области разработки корпоративных приложений за мое предпочтение.
Во-первых, де-факто стандарт — это очень сильное преувеличение. А во-вторых, стандартность подхода довольно хлипкий аргумент.
T>Почему вариант с update/insert будет иметь меньшее кол-во блокировок?
Он будет держать их меньшее время и блокировки будут поддаваться большему контролю.
T>Мне доводилось работать с корпоративными приложениями, в том числе с достаточно крупными (Axapta).
T>Конечно-конечно, довод и разряда "все гандоны, а я супер". Продолжай в том же духе.
Это ты про себя сейчас? ) Не надо, не продолжай.
Здравствуйте, Tissot, Вы писали:
T>Потому что реляционные операторы это — объединение, пересечение, разность, произведение, сокращение, проекция, соединение, деление. Другие в реляционную алгебру не входят.
Я тебе больше скажу, SQL вообще мало отношения к реляционной алгебре имеет. Только работают все с SQL-ем, а не с реляцонной алгеброй в вакууме.
Ты что сказать-то хотел?
T>Я считаю этот вопрос стоит разделить на 2: T>1) ER vs ObjectModel: мой выбор однозначно в пользу ObjectModel. Потому что она банально предоставляет больше возможностей.
Если бы это было так, то весь мир уже давно бы сидел на ООБД. Правда заключается в том, что большинство возможностей ОО подхода при работе с данными оказываются либо бесполезными, либо вообще вредными.
T> Кроме того, er может быть смоделирована через object model, обратное — неверно.
Приведи мне пример объектов, которые нельзя было бы смоделировать, через ER?
T>2) SQL DML vs unit of work: unit of work в большинстве случаев удобнее и предпочтительнее.
Это в каких, например?
T> Единственный его недостаток — это когда нужно массово обновить большое количество объектов.
Если под "массово обновить большое количество объектов" понимается, что объектов больше одного, то согласен.
Проблема только в том, что таких случаев в реальной жизни подавляющее большинство, отсюда делаем совершенно закономерный вывод, что UoW конструкция бесполезная.
Здравствуйте, Tissot, Вы писали:
T>В случае, если действуем по вашему сценарию — сразу получаем exclusive и удерживаем ее на время операции.
Это с какого перепуга?
T>И какой вариант после этого более легковесный?
конечно сиквельный, так как все тяжелые операции будут сделаны еще до первого обращения к базе, без каких либо блокировок.
T>Очень интересно. Откуда же сервер узнает, что блокировку можно отпустить, что она более не понадобится?
Потому что ему доступна вся транзакция и он может ее оптимизировать так как ему удобно.
T>Во-вторых, на самом деле все может быть сложнее: для каких-то категорий клиентов/товаров вполне может быть позволено заказывать в минуса, для каких-то категорий товаров должен гарантировано оставаться какой-то остаток на складах, если заказ крупный, то должно быть проверено, внесена ли предоплата и т.д. и т.п.. Я бы не хотел поддерживать код в котором все эти правила внесены в один update.
В том-то и дело, что теоретически этот update можно поддерживать легко и непринужденно, если делать это так же, как LINQ позволяет обращаться с select-ом.
T>Фишка в том, что это не код, а фикция.
Это твой основной аргумент? )
T>Ты забыл добавить "если update — это единственная действие в бизнес-транзакции".
Бизнес-транзакция тут непричем. Бизнес-транзакция и транзакция БД — вещи совершенно разные и декларативный подход, при работе с данными дает гораздо больший контроль и над тем что происходит в бизнес-транзакциях и над тем, что происходит в БД.
T>Ну в общем-то так и думал. Работал с таким — лучше чем ничего, но все равно недостаточно удобно. Если придумаете что-то более удобное — то честь вам и хвала.
Достаточно того, что это намного удобнее чем UoW, ORM и прочие приседания вокруг ОО.
Здравствуйте, IB, Вы писали:
T>>В случае, если действуем по вашему сценарию — сразу получаем exclusive и удерживаем ее на время операции. IB>Это с какого перепуга?
Потому что update
T>>И какой вариант после этого более легковесный? IB>конечно сиквельный, так как все тяжелые операции будут сделаны еще до первого обращения к базе, без каких либо блокировок.
Не надо менять условия задачи, чтобы подогнать желаемый результат.
T>>Очень интересно. Откуда же сервер узнает, что блокировку можно отпустить, что она более не понадобится? IB>Потому что ему доступна вся транзакция и он может ее оптимизировать так как ему удобно.
Я не знал, что если в батче содержится несколько стейтментов, то сервер анализирует весь батч целиком. Всегда был уверен, что он выполняет стейтмент за стейтментом.
Если у вас другая информация, не мог бы ты подилиться источником оной?
T>>Во-вторых, на самом деле все может быть сложнее: для каких-то категорий клиентов/товаров вполне может быть позволено заказывать в минуса, для каких-то категорий товаров должен гарантировано оставаться какой-то остаток на складах, если заказ крупный, то должно быть проверено, внесена ли предоплата и т.д. и т.п.. Я бы не хотел поддерживать код в котором все эти правила внесены в один update. IB>В том-то и дело, что теоретически этот update можно поддерживать легко и непринужденно, если делать это так же, как LINQ позволяет обращаться с select-ом.
Ключевое слово выделено. А на практике?
T>>Фишка в том, что это не код, а фикция. IB>Это твой основной аргумент? )
А как еще назвать ситуацию, когда в качестве аргумента приводят код, который в жизни практически не встречается?
T>>Ты забыл добавить "если update — это единственная действие в бизнес-транзакции". IB>Бизнес-транзакция тут непричем.
Я в курсе. Под бизнес-транзакцией в данном случае я имел в виду некоторое действие производимое с системой атомарное с точки зрения пользователя. Не смог подобрать более подходящего названия.
Приведенный код был слишком простым, чтобы на его примере увидить недостатки подхода "делаем все одним update-ом". Де-факто этот единственный update потребует как минимум проверку валидности, проверку безопасности, аудит действий и т.д. Если бы все эти сервисные операции были включены в пример, то его красота сразу бы куда-то улетучилась.
IB>Бизнес-транзакция и транзакция БД — вещи совершенно разные и декларативный подход, при работе с данными дает гораздо больший контроль и над тем что происходит в бизнес-транзакциях и над тем, что происходит в БД.
Очень интересный тезис, но позволь в него не поверить. Ну или по-крайней мере объясни откуда возьмется этот больший контроль.
Здравствуйте, IB, Вы писали:
T>>Не стоит выдавать де-факто стандарт в области разработки корпоративных приложений за мое предпочтение. IB>Во-первых, де-факто стандарт — это очень сильное преувеличение.
Я довольно регулярно слежу за тенденциями в этой сфере. И мне как-то последние годов несколько очень редко попадаются апологеты подхода "запихнем все в базу". Все больше как-то говорят за полноценную domain model.
Но не исключаю, что я что-то упустил. Буду благодарен, если подкинешь ссылочек.
IB>А во-вторых, стандартность подхода довольно хлипкий аргумент.
Стандартность означает распространенность, распространенность означает что большинство граблей уже найдено и найдены способы как с ними бороться.
В сухом остатке получаем, что подход с domain model более "проработан" чем иные.
Это тоже будешь считать более хлипким аргументом?
T>>Почему вариант с update/insert будет иметь меньшее кол-во блокировок? IB>Он будет держать их меньшее время и блокировки будут поддаваться большему контролю.
Я же привел пример:
1. делаем update 1 поля
2. долго что-то считаем
3. делаем следующий update
Не мог бы ты оставаясь в рамках этого примера описать, откуда возникнет "меньшее время" и "больший контроль"?
T>>Мне доводилось работать с корпоративными приложениями, в том числе с достаточно крупными (Axapta). IB>
Что тебя так развеселило? Расскажи, вместе посмеемся.
Здравствуйте, IB, Вы писали:
T>>Ну в общем-то так и думал. Работал с таким — лучше чем ничего, но все равно недостаточно удобно. Если придумаете что-то более удобное — то честь вам и хвала. IB>Достаточно того, что это намного удобнее чем UoW, ORM и прочие приседания вокруг ОО.
Спасибо за развернутый и аргуметированный ответ. Вы раскрыли мне глаза.