Re[2]: [ANN] Emit Mapper
От: mrTwister Россия  
Дата: 04.01.10 10:31
Оценка: +1
Здравствуйте, IT, Вы писали:

IT>Совсем не плохо.

Спасибо!

IT>Там где можно дженерик методы можно было бы переписать следующим образом:

IT>
IT>static class MapperImpl<TFrom,TTo>
IT>{
IT>    public static ObjectsMapper<TFrom,TTo> Instance = new ObjectsMapper<TFrom, TTo>(
IT>        new ObjectsMapperManager().GetMapperImpl(
IT>            typeof(TFrom),
IT>            typeof(TTo),
IT>            DefaultMapConfig.Instance
IT>        )
IT>    );
IT>}
IT>public ObjectsMapper<TFrom, TTo> GetMapper<TFrom, TTo>()
IT>{
IT>    return MapperImpl<TFrom,TTo>.Instance;
IT>}
IT>

IT>Тогда время на инициализацию и получение мапперов можно свести практически в абсолютный ноль.


Вообще, идея очень интересная, но к сожалению в случае с EM неприменима, так как для одних и тех же типов могут существовать разные конфигурации мэпперов.

IT>Этот подход ведёт в никуда. Пользователям нужна готовая функциональность, а не потенциальная возможность. Это я как собаковод говорю. "ничто не мешает реализовать" в 99.99% случаев означает, что никто ничего реализовывать никогда и не будет. Более того, разработчик инструмента должен держать своё мнение о моветонах при себе. Учить пользователей жизни — это не его работа. Нормальное объяснение отсутсвия какой-либо функциональности в инструменте это: лень, нет времени, технические проблемы, отсутствие выгоды и т.п.


Вообще, вопрос выбора между "рыбой и удочкой" непростой и не содержит простого однозначного ответа. Всего при реализации некоторых прикладных библиотек мне видятся следующие стратегии:

1) Стратегия "Рыба". Предугадать возможные варианты использования и реализовать под них готовую функциональность. (это как раз то, о чем ты говоришь)
2) Стратегия "Удочка". Предоставив пользователю интерфейс с помощью которого он может самостоятельно добиться желаемого результата. Тем самым автоматически покрывается большинство вариантов использования и даже те из них, о существовании которых автор библиотеки даже не подозревает.

Каждая из стратегий имеет свои достоинства и недостатки, соответственно исходить надо из задачи. Задача Object2Object mapping — это задача с очень смутными и неопределенными вариантами использования из-за того, что их очень много. Ведь сопоставление полей объектов можно выполнять кучей разных способов: можно основываться на именах и при этом либо учитывать регистр, или нет, учитывать префиксы типа "m_", "_" или нет, можно основываться на атрибутах (но этот метод не всегда применим и весьма негибок), можно основываться на специальных XML схемах — на чем угодно, на что может хватить фантазии разработчика (я уже не говорю о более сложных задачах типа преобразования типов, коллекций, дженериков, пост/пре обработки и т.д.). Трезво оценивая свои возможности я понял, что не смогу в приемлимые сроки спроектировать и реализовать интерфейс таким образом, чтобы он покрыл все эти варианты использования, а также те, о которых я ещё не подумал. Вместо этого появилась идея разработать библиотеку, которая с минимальными усилиями позволяет разработчику самому реализовать те варианты ипользования, которые ему нужны, причем именно так, как он себе и представляет идеальным образом. Обрати внимание, что когда был надан вопрос на счет атрибутов мне было не лень продемонстрировать всего несколько строчек кода, реализующие мэппинг через атрибуты. Собственно для этого библиотека и создавалась.

IT>Как раз как маппер, в реальных сценариях BLT даст фору кому угодно.

Смотря на каких сценариях. Если это сценарии связанные с БД, то скорее всего да, ведь он именно на эти сценарии и заточен. Если же рассматривать сценарии Object2Object, то уже вряд ли, так как БЛТ сильно уступает конкурентам по функционалу и количеству покрываемых вариантов использования.

IT>"Даже вложенные объекты" в BLT не поддерживаются по-умолчанию. Кстати, они не поддерживаются по-умолчанию практически нигде,

Ну почему же, AutoMapper поддерживает, и многие другие Object2Object mapping библиотеки поддерживают. Я бы даже сказал, что большинство из них.

IT>т.к. это вызывает больше проблем, чем бенефитов.

Все проблемы решаемы.

IT>Научить BLT справляться с поставленной задачкой можно, например, следующим образом:


IT>
IT>[MapField("str2", "i.str2")]
IT>public class Destination
IT>{
IT>    public class Int
IT>    {
IT>        public string str2;
IT>    }

IT>    public string str1;
IT>    public Int i = new Int();
IT>}

IT>[MapField("str2", "i.str2")]
IT>public class Source
IT>{
IT>    public class Int
IT>    {
IT>        public string str2 = "B1::Int::str2";
IT>    }

IT>    public string str1 = "B1::str1";
IT>    public Int i = new Int();
IT>}
IT>


Ну, это уже "закат солнца вручную". Если уж нет автоматики, не проще ли тогда вообще написать:
Destination.i.str2 = Source.i.str2;

Тут по крайней мере и интеллисенс есть и компилятор ошибки проверит.

IT>Думаю, любому взрослому и умному человеку, заинтересованному в конечном результате, обязательно захотелось бы найти объснению такому чудовищному отставанию в производительности и разобраться что же происходит на самом деле,

Ну это очевидно, я же выше по теме написал из-за чего разница в производительности.

IT>а не отмазываться фразами вроде "Дак это проблемы BLT. Он другого интерфейса не предоставляет.". Тем более, что и интерфейс такой у BLT есть

Как это есть? Ты же сам пишешь, что:

API для получения мэппера как это происходит в сценарии в BLT попросту отсутствует.


IT>и у EM есть интерфейс, который поставил бы библиотеки в одинаковые условия. И тогда 250 раз мгновенно уменьшились бы на 125. Хотя проблема, конечно, не в этом.


А зачем их ставить в одинаковые условия? Это примерно как при сравнении производительности бейсика и С++ код на С++ интерпретировать дабы поставить их в одинаковые условия и тем самым доказать, что бейсик не медленее С++.

EmitMapper проектировался в первую очередь как Object2Object mapper. В этом случае схемы данных источников и получателей известны заранее и нет проблемы с тем, чтобы создать конфигурацию мэппера под конкретные схемы данных заранее (например при старте программы) и затем её только использовать. Посмотри выше по теме здесь
Автор:
Дата: 30.12.09
- человек именно так и делает.

IT>Сценариев где нужен маппинг одного объектв в другой не так много, высокопроизводительных сценариев ещё меньше, а когда они реально возникают, то написать код вручную, особенно сегодня при наличии инициализаторов в C# не представляет особой сложности.


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

IT>В общем, BLT под такие сценарии никогда не оптимизировался. Другое дело вот такие сценарии:


IT>
IT>...
IT>

IT>Здесь мы уже имеем разницу не в 250 раз, а около двух, хотя и не в пользу BLT.
У меня получается разница в 4-5 раз. Ну да неважно, это уже более-менее сравнимо.

IT>Оставшаяся разница объясняется тем, что BLT делает больше проверок в рантайм. Например, если изменить приведённый выше код следующим образом, то EM упадёт:


IT>
IT>public class B2
IT>{
IT>    ...
IT>    public decimal? n5 = null;
IT>    ....
IT>}
IT>


И это очень зря, что БЛТ проглотил данную явно ошибочную ситуацию. Что он поставит вместо null? 0? А почему не -1 или не 42?
EM поддерживает NullSubstitution и с помощью следующей конфигурации можно создать мэппер, который вместо null будет писать 42:
var mapper = ObjectsMapperManager.DefaultInstance.GetMapper<B2, A2>(
        new DefaultMapConfig().NullSubstitution<decimal?,int>( state => 42 )
    );

Этот мэппер уже не падает.

IT>Некоторые из этих проверок можно сократить только генерацией кода под каждый конкретный вариант использования, что практически не имеет смысла если не кешировать каким-то образом сам вариант использования или явно не напрягать пользователя, что бы он его как-то идентифицировал. В приведённых выше тестах EM именно этим и занимается.


К сожалению, без этого нелься сделать сколько-нибудь гибкий мэппер.

IT>Это хорошо работает с объектами, но не работает с источниками данных, структура которых становится известной только в момент выполнения. К таким источникам относятся, например, базы данных.


Согласен.

IT>Кстати, давайте посмотрим что у нас с базами данных:

IT>
IT>...
IT>

IT>В этом примере EM уже начинает отставать.
Верно, но после маленькой оптимизации (заменил DbDataReader[String] на DbDataReader[Int]) EM уже начал обгонять BLT.

IT>Скорее всего reader.ToObjects, взятый из примеров библиотеки не очень хорош.

Во-первых, EM — это не про базы данных. Конфигурация мэппера для дадатидера была показана для примера. Во-вторых, отставание было на считанные проценты на синтетическом примере, при котором вызов "SqlCommand.ExecuteReader" по результатам профилирования составляет всего 4% от общего времени работы программы. В-третьих это уже поправлено

IT>При этом он не очень хорош будучи написанным самим автором библиотеки, а что будет написано, если будет, пользователями, плохо знакомыми как с самой библиотекой, так и с базами данных?


Ничего страшного не случилось бы. Если для кого-то несколько процентов в абсолютной разнице в производительности между DbDataReader[String] и DbDataReader[Int] очень существенны, то, возможно, вместо базы данных следовало бы использовать что-то другое? (потому как судя по всему СУБД простаивает без дела).

IT>Ну и в заключении, вот такие примеры в качестве сценарии использования библиотеки из документации лучше убрать:


IT>
IT>public DTOCustomer GetCustomer(Guid customerId)
IT>{
IT>    using (var dc = new DataContext())
IT>    {
IT>        var customer = dc.Customers.Where(c => c.CustomerID == customerId).Single();
IT>        return ObjectsMapperManager.DefaultInstance.GetMapper<Customer, DTOCustomer>().Map(customer);
IT>    }
IT>}
IT>


А что с ним не так?

IT>И в самом самом заключении, ObjectsMapperManager — это немного не по-английски.

Ок
лэт ми спик фром май харт
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.