Здравствуйте, 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 — это немного не по-английски.
Ок