|
|
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
Доброго дня, коллеги) Меня вот интересует вопрос, есть ли ORM удовлетворяющая следующим требованиям: 1. Отсутствие сессий, работа с сущностями по принципу (ProductRepository.Insert(product) /Update(product). т.е. во первых: вся каскадность ложится на плечи репозитория а не задается в маппинге. во вторых: Все изменения в базу данных обозначаются в явном виде. 2. Самодостаточность (Нет необходимости вручную писать запросы к БД). Т.е. генерация SQL 3. Открытый исходный код, или хотя бы возможность его купить (очень не маловажная деталь). Т.е. вообще я представляю себе идеальную ORM как гибрид датасета с NHibernate. от NHibernate оставить критерии и идею маппинга (так чтоб небыло необходимости наследоваться от каких-то классов). А принцип сохранения и загрузки аля DataSet. Ваше мнение? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 06:26 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkitВаше мнение? Мое мнение трудно составить, т.к. трудно сразу так понять требования. Они, честно говоря, сумбурные. Но на ассоциативном уровне мне представляется, что Вам бы подошла не ОРМ а чисто объектная СУБД. Например CACHE или Versant. Последняя из них (Versant FastObjects) красиво интегрирована с Visual Studio. Достаточно поставить классу атрибут [Persistent], как он начинает "жить" в БД. И тут Вам не придется утомлять себя даже конструкциями типа "ProductRepository.Insert(product)". Просто исполните "new Product()" и объект тут же попадет в БД. Но вот про открытость исходных кодов я не уверен... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 09:19 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkit, Моё мнение: описанные требования - голубая, но несбыточная мечта программиста. Про NHibernate: 1. Причин, по которым появились сессии несколько (хотя бы Identity map и Lazy load), и от них никуда не деться. Но вы можете закрывать сессию сразу по завершении своего запроса. Каскадность: Как по вашему должна определяться "глубина каскадирования"? А то ведь автоматом можно всю базу удалить. 2. Необходимости писать запросы нет, но иногда это бывает полезно. 3. Очень даже открытый. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 11:44 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkitА принцип сохранения и загрузки аля DataSet. Нахрена? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 15:24 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
МСУsibkitА принцип сохранения и загрузки аля DataSet. Нахрена? Это даст возможность в ручную определять что сохранять, а что удалять и как. Не гоняя циклом сущности в интерцепторе. Что касается каскадности, то имеется ввиду каскадность при сохранении удалении. В NHibernate это не плохо работает, но недавно мне пришлось сталкнуться с итуацией, когда при разных обстоятельствах нужна по разному работающая каскадность (В итоге я всю каскадность реализовывал через интерцептор). С полезностью писать запросы самому я не соглашусь. Т.к. анализируя последние месяцы работы понимаю, что написал бы их уже миллион (И это самый большой минус датасета кстати). Nhibernate-вские критерии позволяют очень просто генерировать запросы при этом довольно качественно. (Хочу тут похвастоваться, что исправил баг в исходниках NHibernate, не позволяющий использовать одну таблицу в одном запросе несколько раз :)). Объектные СУБД не устраивают в плане того, что не всегда приходится всё делать с нуля и очень часто попадаются проекты под уже спроектированную и заполненную базу данных. Собственно NHibernate это то в чем я сейчас, и уже достаточно давно работаю, но у него есть большие минусы. Во первых: Я не понимаю как масштабировать проект с его использованием (Думаю, что никак). Т.е. когда библиотека DAO с маппингом и всем вытекающим не одна (Пример: Компания ведет деятельность по разным направлениям, соответственно касса одна, менеджеры одни, клиенты одни, а вот логика CRM систем совершенно разная) Было бы идеально реализовать возможность дополнения маппингом уже существующие библиотеки. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 20:08 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
Во вторых: NHibernate очень не прозрачно отслеживать все изменения. Гораздо удобнее делать Update(entity), чем Flush(). В третьих: к NHibernate приходится писать много костылей, он очень мощный, но куда не капни поглубже везде есть грабли свои (Например сейчас столкнулся с проблемой абстрактных классов - загружаю объект абстрактного класса, а он прокси и приходится педальным способом выяснять что это за объект) Собственно к чему я завел эту тему. Буквально пол года назад мне кто-то сказал, что пишет свою ORM - тогда я над ним посмеялся. А сейчас поработав и полностью проникнувшись двумя технологиями у меня уже накопилось достаточно идей, как можно собрать самое лучшее из DataSet и NHibernate. Просто очень не хочется изобретать велосипед. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 20:19 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkit Это даст возможность в ручную определять что сохранять, а что удалять и как. Не гоняя циклом сущности в интерцепторе. Что касается каскадности, то имеется ввиду каскадность при сохранении удалении. В NHibernate это не плохо работает, но недавно мне пришлось сталкнуться с итуацией, когда при разных обстоятельствах нужна по разному работающая каскадность (В итоге я всю каскадность реализовывал через интерцептор). Бывают случаи, когда хибер не применим (или плохо применим). К примеру, обновлять 100 000 записей будет мучительно долго. Но я бы повнимательнее посмотрел на ваша архитектуру, может в ней проблема. sibkit С полезностью писать запросы самому я не соглашусь. Т.к. анализируя последние месяцы работы понимаю, что написал бы их уже миллион (И это самый большой минус датасета кстати). Иногда такие случаи бывают, когда проще решить именно запросом. Ясное дело для банальных CRUD Nhibernate гораздо удобнее. sibkit Собственно NHibernate это то в чем я сейчас, и уже достаточно давно работаю, но у него есть большие минусы. Во первых: Я не понимаю как масштабировать проект с его использованием (Думаю, что никак). Т.е. когда библиотека DAO с маппингом и всем вытекающим не одна (Пример: Компания ведет деятельность по разным направлениям, соответственно касса одна, менеджеры одни, клиенты одни, а вот логика CRM систем совершенно разная) Было бы идеально реализовать возможность дополнения маппингом уже существующие библиотеки. Что вы понимаете под масштабированием? Что мешает замапить существующие библиотеки? sibkit Во вторых: NHibernate очень не прозрачно отслеживать все изменения. Гораздо удобнее делать Update(entity), чем Flush(). В третьих: к NHibernate приходится писать много костылей, он очень мощный, но куда не капни поглубже везде есть грабли свои (Например сейчас столкнулся с проблемой абстрактных классов - загружаю объект абстрактного класса, а он прокси и приходится педальным способом выяснять что это за объект) Что мешает делать Session.SaveOrUpdate()? Зачем выяснять настоящий тип абстрактного класса? Вы про SOLID принципы слышали? sibkit Собственно к чему я завел эту тему. Буквально пол года назад мне кто-то сказал, что пишет свою ORM - тогда я над ним посмеялся. А сейчас поработав и полностью проникнувшись двумя технологиями у меня уже накопилось достаточно идей, как можно собрать самое лучшее из DataSet и NHibernate. Просто очень не хочется изобретать велосипед. Давайте. Всласть поработав над своим маппером, поймёте, почему nhibernate такой, какой он есть. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.08.2010, 22:55 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
SolYUtorsibkit Это даст возможность в ручную определять что сохранять, а что удалять и как. Не гоняя циклом сущности в интерцепторе. Что касается каскадности, то имеется ввиду каскадность при сохранении удалении. В NHibernate это не плохо работает, но недавно мне пришлось сталкнуться с итуацией, когда при разных обстоятельствах нужна по разному работающая каскадность (В итоге я всю каскадность реализовывал через интерцептор). Бывают случаи, когда хибер не применим (или плохо применим). К примеру, обновлять 100 000 записей будет мучительно долго. Но я бы повнимательнее посмотрел на ваша архитектуру, может в ней проблема. Проблема не в скорости, а в том что в некоторых ситуациях необходимо каскадно удалять дочерние объекты, а в некоторых нет - это логика такая и к архитектуе отношения не имеет. SolYUtor sibkit С полезностью писать запросы самому я не соглашусь. Т.к. анализируя последние месяцы работы понимаю, что написал бы их уже миллион (И это самый большой минус датасета кстати). Иногда такие случаи бывают, когда проще решить именно запросом. Ясное дело для банальных CRUD Nhibernate гораздо удобнее. Проще не значит лучше, принципиально не пишу вручную запросы, за исключением вызова хранимых процедур и получения данных из неразмапленных таблиц. И критериями можно разрулить далеко не банальные вещи. SolYUtor sibkit Собственно NHibernate это то в чем я сейчас, и уже достаточно давно работаю, но у него есть большие минусы. Во первых: Я не понимаю как масштабировать проект с его использованием (Думаю, что никак). Т.е. когда библиотека DAO с маппингом и всем вытекающим не одна (Пример: Компания ведет деятельность по разным направлениям, соответственно касса одна, менеджеры одни, клиенты одни, а вот логика CRM систем совершенно разная) Было бы идеально реализовать возможность дополнения маппингом уже существующие библиотеки. Что вы понимаете под масштабированием? Что мешает замапить существующие библиотеки? Что касается "масштабирования" - здесь я возможно не правильно выразился. Имеется ввиду наращивание функционала всей системы в целом (включающей несколько проектов), без изменения работающих проектов и с возможностью использовать существующий код (в виде dll библиотек) SolYUtor sibkit Во вторых: NHibernate очень не прозрачно отслеживать все изменения. Гораздо удобнее делать Update(entity), чем Flush(). В третьих: к NHibernate приходится писать много костылей, он очень мощный, но куда не капни поглубже везде есть грабли свои (Например сейчас столкнулся с проблемой абстрактных классов - загружаю объект абстрактного класса, а он прокси и приходится педальным способом выяснять что это за объект) Что мешает делать Session.SaveOrUpdate()? Зачем выяснять настоящий тип абстрактного класса? Вы про SOLID принципы слышали? Причем здесь SaveOrUpdate? Если сущность в сессии, то при флаше изменения в ней сохраняются в базу. независимо от того делался ей Session.SaveOrUpdate или Session.Update. Читайте теорию. Что касается абстрактных классов, то требуется не просто выяснить тип, а дозагрузить этот класс. Сейчас я это делаю вот таким вот костылем: if (line.Subject.SubjectTypeIndex==1) { CalculationRecurciveSubject cps = session.Get<CalculationRecurciveSubject>(line.Subject.Id); } возможно, это оттого, что у меня не самая последняя версия, но тем не менее, приходится вставлять костыли. А вот ваш вопрос "Зачем выяснять настоящий тип абстрактного класса?" считаю глупым. SolYUtor sibkit Собственно к чему я завел эту тему. Буквально пол года назад мне кто-то сказал, что пишет свою ORM - тогда я над ним посмеялся. А сейчас поработав и полностью проникнувшись двумя технологиями у меня уже накопилось достаточно идей, как можно собрать самое лучшее из DataSet и NHibernate. Просто очень не хочется изобретать велосипед. Давайте. Всласть поработав над своим маппером, поймёте, почему nhibernate такой, какой он есть. Прекрасно представляю объем работы который придется совершить. Nhibernate далеко не совершенен. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 07:22 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkit Прекрасно представляю объем работы который придется совершить. Nhibernate далеко не совершенен. Вот было бы здорово, если б Вы сделали свою систему ОРМ без использования ADO.NET, а круче! А то меня не оставляет чувство досады по поводу того, что если б я делал проект на хорошо изученном и проверенном опытом ADO.NET, то давно бы успешно его сдал, причем лучшего качества, чем на Nhibernate, который все равно является "оберткой" для ADO.NET. "Nhibernate далеко не совершенен" - это еще мягко сказано! Я так и не приручил его к ораклу в полной мере. Еще удручают заявления типа: "Опытные пользователи Nhibernate избегают композитных первичных ключей". Какого, спрашивается, они их избегают, если композитные первичные ключи вытекают из логики предметной области?!! Где это видано, чтобы структуру данных адаптировали под инструмент доступа к ней? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 11:01 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
Курдль, на этапе проектирования схемы данных никто не запрещает прикрутить первичный ключ, вместо накладывания композитов. Весьма правильная практика, удовлетворяющая второй нормальной форме БД . P.S. А, вообще, тема ниачём. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 11:15 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
МСУКурдль, на этапе проектирования схемы данных никто не запрещает прикрутить первичный ключ, вместо накладывания композитов. Весьма правильная практика, удовлетворяющая второй нормальной форме БД . Здесь 2 заявления. 1. "на этапе проектирования схемы данных никто не запрещает прикрутить первичный ключ". Да, сам Э.Кодд бы этого не запретил. Но суррогатный первичный ключ, получается, не нужен никому, кроме NHibernate. 2. "Весьма правильная практика, удовлетворяющая второй нормальной форме БД ". А почему не удовлетворяющая третьей? И кто сказал, что она правильная? Проектировщики БД пыхтят над идеальным отображением предметной области в БД не ради удобства ОРМ и даже разработчиков ППО. Их задача - создать такую структуру, которая бы не позволила никому, включая упомянутых разработчиков, нарушить логику взаимосвязи между данными. И их не касается, что там за ОРМ на "другом конце провода". Кроме того, не все проекты начинаются с чистого листа. Многим приходится работать с уже спроектированной структурой БД, в которой композитные ПК никому не мешали. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 11:38 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
КурдльДа, сам Э.Кодд бы этого не запретил. Но суррогатный первичный ключ, получается, не нужен никому, кроме NHibernate. Да никто ничего не запрещает, увольте. Я говорю про вариант решения, причём правильный вариант. КурдльА почему не удовлетворяющая третьей? Потому что: 2НФТаблица находится во второй нормальной форме, если она находится в первой нормальной форме, и при этом любой её атрибут , не входящий в состав возможного ключа, функционально полно зависит от каждого возможного ключа . КурдльИ кто сказал, что она правильная? Карло Заниоло с Бойсом-Коддом. КурдльПроектировщики БД пыхтят над идеальным отображением предметной области в БД не ради удобства ОРМ и даже разработчиков ППО. Их задача - создать такую структуру, которая бы не позволила никому, включая упомянутых разработчиков, нарушить логику взаимосвязи между данными. Каким образом введение первичного ключа (кластерного индекса) может нарушить логику взаимосвязи между данными? КурдльИ их не касается, что там за ОРМ на "другом конце провода". Причём тут ORM? Речь о схеме данных. КурдльКроме того, не все проекты начинаются с чистого листа. Многим приходится работать с уже спроектированной структурой БД, в которой композитные ПК никому не мешали. Именно поэтому я изначально сказал: МСУКурдль, на этапе проектирования схемы данных никто не запрещает прикрутить первичный ключ ... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 12:02 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
МСУ Каким образом введение первичного ключа (кластерного индекса) может нарушить логику взаимосвязи между данными? Никаким. А я этого и не утверждал. Я говорил, что они и создадут структуру без суррогатных ключей. А потом придем мы и их, как Вы сказали, - вкорячим? Прикрутим? "Каким образом бетонная плита в багажнике автомобиля может нарушить логику его работы?" Логику - никаким. Исправную работу на расчетных параметрах - может. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 12:25 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
МСУP.S. А, вообще, тема ниачём. Тема о том, чтобы заменить NHibernate на что-то более управляемое и логически связанное. Вообще когда создавал, надеялся получить описание решения описанных проблем, например, в других мапперах, (т.к. сам все не изучишь - да и смысла нету). Но, возможно уже есть решение, которое мне подойдет - это был бы идеальный вариант. Что касается идеи по поводу создания своего маппера: Я не ставлю перед собой цели создать что-то грандиозно громоздкое, наподобие NHibernate или EF. Вообще есть вот такие мысли: ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 12:46 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
1. Данные хранятся в объектах, которые создаются вручную. 2. Объекты лишены коллекций связанных сущностей (соответственно и лэйзи отпадает). Для получения связей можно использовать например аналог constrain в DataSet. 3. Есть реализация критерий, т.е. объектное представление запроса в БД не привязанное к именам конкретных таблиц, а завязанное исключительно на маппинге. Вот собственно и всё. Иными словами это датасет, только привязанный к критериям по имени классов и с классами вместо строк (что позволяет делать рефакторинг). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 12:55 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
КурдльА потом придем мы и их, как Вы сказали, - вкорячим? Прикрутим? Вы читать умеете или только учитесь МСУна этапе проектирования схемы данных никто не запрещает прикрутить первичный ключ ... ? Курдль"Каким образом бетонная плита в багажнике автомобиля может нарушить логику его работы?" Ещё как нарушит. Большие нагрузки на двигатель и на раму. Первичный ключ вызывает большие нагрузки на БД? КурдльЛогику - никаким. Исправную работу на расчетных параметрах - может. Вот тут поподробнее. sibkitТема о том, чтобы заменить NHibernate на что-то более управляемое и логически связанное. Я не вижу в хибере что-то неуправляемое и несвязанное логически. В любой ORM есть свои достоинства и недостатки. Одних - устраивает, других - нет. Во-вторых, никогда не поздно начать писать SQL-запросы вручную. sibkitЧто касается идеи по поводу создания своего маппера: Я не ставлю перед собой цели создать что-то грандиозно громоздкое, наподобие NHibernate или EF. Вообще есть вот такие мысли: Больше времени потеряете, чем пользы будет. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 12:59 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkitДля получения связей можно использовать например аналог constrain в DataSet. DataRelation, а не "constrain" ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 13:00 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
КурдльDataRelation, а не "constrain" Да, верно DataRelation, а еще как вариант динамически создаваемые связи, чтоб в коде выглядело типа: DataStorage ds; Order order; IRelation relation = new Relation<Order,OrderItem>("Id","OrderId"); IList<OrderItem> items = ds.Load(order,relation); ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 13:31 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
sibkit, есть у нас такое, никакого маппинга и т.д., но БД проектировать надо в самой системе :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 14:03 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
МСУЯ не вижу в хибере что-то неуправляемое и несвязанное логически. В любой ORM есть свои достоинства и недостатки. Одних - устраивает, других - нет. Во-вторых, никогда не поздно начать писать SQL-запросы вручную. При всем уважении к NHibernate (я только на нем работаю с 2007-го) Было написано очень много всяческих костылей к нему, саморучно исправлены баги и изучен он мной просто вдоль и поперек. При этом вспоминая работу с ADO.NET вспомнил всю гибкость, при построении бизнес логики и удовольствие от полного контроля загрузки и сохранения данных. ИМХО лэйзи загрузки это такой напантованный механизм, который при первом взгляде красив и чертовски удобен, а при разработке сложных проектов становится непосильной ношей - у меня половина времени разработки уходит на отслеживание и проверки где там что загрузилось как сохранилось. + так или иначе NHibernate тянет лишние данные из базы. А при сложной структуре данных работа с ним превращается в какой-то ад. МСУБольше времени потеряете, чем пользы будет. Пользу сложно переоценить. Естественно, что тут, человеко-месяцем разработки не отделаешься, даже для самой примитивной реализации. И очень бы хотелось найти что-нибудь уже готовое (помоему раз четвертый это уже повторяю :)) Но когда на кону стоят проекты в 1000 человеко-месяцев, думаю 10-20 из них целесообразно выделить на разработку механизма, позволяющего прозрачно и гибко работать с данными. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 14:04 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
ViPRossibkit, есть у нас такое, никакого маппинга и т.д., но БД проектировать надо в самой системе :) А что это такое? можно подробнее? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 14:10 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
ViPRossibkit, есть у нас такое, никакого маппинга и т.д., но БД проектировать надо в самой системе :) из описания типов, макротипов, классификаторов, макроклассификаторов,... генерируется пользовательский интерфейс (который может изменить пользовател с соттветсивующими правами), в интерфейс выводятся методы соответствующих типов, макротипов, классификаторов, макроклассификаторов, имеются стандартные методы применимые ко всем примитивам, всякие там пейджинги, лейзи загрузки и т.д, автоматическое обновление БД с разными режимами и т.д. Основные функции для прогера - ЗагрузитьТип, ЗагрузитьМакротип, ЗагрузитьТипПоССылке, СоздатьИнтерфейс и т.д. Прогер пишет только методы, где их искать задается в описании типов, макротипов, классификаторов, макроклассификаторов. Есть встроенный редактор кода для написания методов. вот вкраце. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 14:16 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
ViPRos, Сахаватушка, Вы опять гавноскрины свои постите? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 14:21 |
|
||
|
ORM с требованиями
|
|||
|---|---|---|---|
|
#18+
МСУ, отдохни ка чуток, вон сколько тем вокруг ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 25.08.2010, 14:24 |
|
||
|
|

start [/forum/topic.php?fid=17&msg=36810099&tid=1351116]: |
0ms |
get settings: |
10ms |
get forum list: |
17ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
157ms |
get topic data: |
9ms |
get forum data: |
2ms |
get page messages: |
61ms |
get tp. blocked users: |
2ms |
| others: | 208ms |
| total: | 472ms |

| 0 / 0 |
