Re[7]: [ANN] Emit Mapper
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 04.01.10 15:14
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Прекрасно. Сколько надо написать кода для маппинга вложенных структур (в том числе рекурсивных)?


Это лучше у IT спросить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1333 on Windows 7 6.1.7600.0>>
AVK Blog
Re[5]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 04.01.10 16:20
Оценка:
Здравствуйте, Holms, Вы писали:

H>теперь ждём DML для LINQ-a

H>большая просьба, не мудрите с этим, сделаете просто как в SubSonic, а не как в Linq2Sql от MS.

А как сделано в SubSonic? Можно ссылочку на примеры?
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: [ANN] Emit Mapper
От: Holms США  
Дата: 04.01.10 16:45
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Holms, Вы писали:


IT>А как сделано в SubSonic? Можно ссылочку на примеры?

хочется такого что-то в этом роде

1.
Person p;
db.Person.Insert(p = new Person(){...});
Console.WriteLine(p.Id);
2.
db.Update<T>(T p, int pkValue, Func<T, string> pkNameGetter);
здесь так-же хочется что-бы не посылать все поля а только те которые нужны.
3.
db.Delete<T>(int pkValue, Func<T, string> pkNameGetter);

желательно что-бы была возможность всё это дело запихнуть в транзакцию.

В остальном я доволен, вчера попробовал кое-что для себя, сработало отлично

А, я не особо искал, но может где-то внутри BL есть функцинальность для определения сущностей БД, т.е. список таблиц, список столбцов,..., ведь для рахных БД это по разному?

Спасибо

P.S. Перехожу на BL, хотя надо ознакомиться с лицензией
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
The life is relative and reversible.
Re[3]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 04.01.10 18:28
Оценка: 8 (1)
Здравствуйте, mrTwister, Вы писали:

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

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

Сделать хотя бы для дефолтных мапперов. Судя по опыту они используются в 99% случаев.

T>Каждая из стратегий имеет свои достоинства и недостатки, соответственно исходить надо из задачи. Задача Object2Object mapping — это задача с очень смутными и неопределенными вариантами использования из-за того, что их очень много.


Именно поэтому легче делать маппинг object to object вручную.

T>Ведь сопоставление полей объектов можно выполнять кучей разных способов: можно основываться на именах и при этом либо учитывать регистр, или нет, учитывать префиксы типа "m_", "_" или нет, можно основываться на атрибутах (но этот метод не всегда применим и весьма негибок), можно основываться на специальных XML схемах — на чем угодно, на что может хватить фантазии разработчика (я уже не говорю о более сложных задачах типа преобразования типов, коллекций, дженериков, пост/пре обработки и т.д.).


Опять же фантазии в 99% случаев хватает только на атрибуты и XML схему. А в Java так вообще только на последнее.

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


Нужно было не лениться, а продемонстрировать. А ещё лучше, чтобы основные сценарии были в библиотеке по умполчанию. Либо умолчания настраивались для всего приложения, а не для каждого сценария.

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

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

Как я уже сказал, проблема в том, что при уж очень заумных сценариях object to object проще написать ручками. Но в принципе, я не возражаю. Возможностей меньше. Хотя больше пока никому не было нужно.

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

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

А сколько их всего O2O? Я знаю, что подавляющее большинство ORM требуют ручной разметки маппинга для каждого поля, не то, что для вложенных.

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

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

Это понятно. Вопрос кем они решаемы, разработчиком библиотеки один раз или пользователем библиотеки в каждом конкретном случае.

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


T>Ну, это уже "закат солнца вручную". Если уж нет автоматики, не проще ли тогда вообще написать:


Проще. Поверь мне в сценариях O2O проще. Автоматика может привести к большим проблемам как я уже сказал. Да и автоматику на всё не сделаешь. Обычно маппинг во вложенные объекты нужно делать из плоских стркутур. Поле BillingAddressCity в BillingAddress.City. И всё, уже пошёл закат солнца.

T>
T>Destination.i.str2 = Source.i.str2;
T>

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

Во! А я о чём?

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

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

Так она же реально не в 250 раз. К тому же ты маппил в один и тот же объект, а BLT создавал каждый раз новые. Одно это уже сокращает разницу в два раза.

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

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

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


Я говорю о маппинге в один и тот же объект.

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


Не надо передёргивать. Тестирование в одинаковых условиях нужно для того, чтобы увидеть реальную разницу и понять проблемы. Тестирование в подогнанных под один инструмент условиях (как, например, на ORMBattle) нужно для того, чтобы показать кто круче. Чувствуешь разницу?

Твой тест достаточно переписать вот так:

static long EmitMapper_Simple(int mappingsCount)
{
    var s = new B2();
    var d = new A2();

    var sw = new Stopwatch();
    sw.Start();

    for (int i = 0; i < mappingsCount; ++i)
    {
        var mapper = ObjectsMapperManager.DefaultInstance.GetMapper<B2, A2>(
            new DefaultMapConfig().NullSubstitution<decimal?,int>( state => 42 ));
        d = mapper.Map(s);
    }

    sw.Stop();
    return sw.ElapsedMilliseconds;
}

И разница станет уже в 5 раз, а не в 250. В реальности он именно так использоваться и будет. А со списками мы уже разобрались. EM позволяет получить маппер вне операции маппинга, согласен, это не плохая фишка. Может быть когда-нибудь и в BLT такая появится, но пока никто не просил, что говорит о ненадобности.

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


Это я понял. С другой стороны ты утвержаешь следующее:

2) Очень гибкая. Проецировать можно что угодно на что угодно. Например, датаридер на объекты, объекты на SQL команды (UPDATE, INSERT), объекты на строку (сериализация), строку на объекты (десериализация) и так далее.


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


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


Это правда. Но в конце концов существуют практики повторного использования кода. Да и добавление нового поля может потребовать подстройки маппера. Так что как раз на практике не всё так страшно.

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


IT>>
IT>>    public decimal? n5 = null;
IT>>


T>И это очень зря, что БЛТ проглотил данную явно ошибочную ситуацию. Что он поставит вместо null? 0? А почему не -1 или не 42?


Это не ошибочная, а самая обычная штатная ситуация. Маппинг long -> int можно было бы назвать ошибочной ситуацией. А с нулевыми значениями всё просто. От источников данных с неизвестной в компайл-тайм стркутурой может приехать всё, что угодно. Даже вот из такай таблицы:

CREATE TABLE MyTable
(
    Field1 int NOT NULL PRIMARY KEY,
    Field2 int NOT NULL
)

можно легко получить NULL в NOT NULL поле:

SELECT Field1, Field2
FROM MyTable WHERE ...
UNION ALL
SELECT NULL, Field2
FROM MyTable WHERE ...

Можно в XML пустую строчку в поле получить, можно в одном объекте поле получить, а в следующем нет, и т.п.

В общем, такая ситуация для BLT самая штатная. И по идее ошибкой является решение её не обрабатывать по умолчанию. Хотя для O2O может и сойдёт. Что поставить вместо null, конечно же, вопрос и его надо решать.

T>EM поддерживает NullSubstitution и с помощью следующей конфигурации можно создать мэппер, который вместо null будет писать 42:

T>
T>var mapper = ObjectsMapperManager.DefaultInstance.GetMapper<B2, A2>(
T>        new DefaultMapConfig().NullSubstitution<decimal?,int>( state => 42 )
T>    );
T>

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

Не падает. Только очень многословно получается. Мне это нужно при каждом маппинге выписывать или можно задать где-то одно правило на все варианты использования в программе?

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


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


Быстрый. Не гибкий, а быстрый. Гибкость в дизайне библиотеки. Там же удобство её использования.

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


T>Согласен.


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

IT>>
IT>>...
IT>>

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

Я уже хотел было обрадоваться. К сожелению, если написать два теста вот с такими командами:

cmd.CommandText = "SELECT * FROM Customers";        // Test1
cmd.CommandText = "SELECT Phone, * FROM Customers"; // Test2

то после вызова первого теста второй отработает неправильно.

В принципе, такой подход будет работать с линковскми CompiledQuery. Я как раз собираюсь его как-нибудь реализовать на досуге, если будет время.

Кстати, не думал в реализации GetValuesGetter попробовать убрать инициализацию fieldNum через замену делегата на лету. Будет, конечно, дополнительный косвенный вызов, но если кроме fieldNum ещё и тип поля подсмотреть, то можно будет вызывать не reader.GetValue, а, например, reder.GetInt32 и полностью избавиться от боксинга.

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

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

Но как мы видим, нужно продолжать работать.

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


Странно такое слышать именно от тебя. С одной стороны ты говоришь о высокой производительности, с другой потеря нескольких процентов фигня. Мне почему-то казалось что из несколько процентов туда-сюда и складывается общая производительность в целом.

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>>


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


Во-первых, многословно. Выписывать столько кода... Я бы предпочёл такую обёртку:

Map.Object2Object<Customer,DTOCustomer>(customer);

или даже

Map.To<DTOCustomer>.From(customer);

Во-вторых, в данном примере происходит двойной маппинг, что уже настолько сажает быстродействие, что о нём можно больше не говорить.
В-третих, твои пользователи первым делом засунут сюда анонимные типы, а с ними EM как я понял не работает совсем.
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 04.01.10 18:45
Оценка:
Здравствуйте, Holms, Вы писали:

H>хочется такого что-то в этом роде


H>1.

H>Person p;
H>db.Person.Insert(p = new Person(){...});
H>Console.WriteLine(p.Id);

Такая функциональность есть см. SqlQuery, только без p.Id. Она делается без линка. Для Insert от линка интересно прежде всего получить следующее:

INSERT INTO (...)
SELECT ... FROM ...


H>2.

H>db.Update<T>(T p, int pkValue, Func<T, string> pkNameGetter);
H>здесь так-же хочется что-бы не посылать все поля а только те которые нужны.

Такое не интересно. Нужна семантика в рамках линка без строковых констант.

H>3.

H>db.Delete<T>(int pkValue, Func<T, string> pkNameGetter);

H>желательно что-бы была возможность всё это дело запихнуть в транзакцию.


Тоже самое, интересен DELETE по условию, а не для одного объекта.

H>А, я не особо искал, но может где-то внутри BL есть функцинальность для определения сущностей БД, т.е. список таблиц, список столбцов,..., ведь для рахных БД это по разному?


Не совсем понял вопроса.

H>P.S. Перехожу на BL, хотя надо ознакомиться с лицензией


MIT. Можешь исходники хоть упаковывать в коробку и продавать.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: [ANN] Emit Mapper
От: Holms США  
Дата: 04.01.10 19:15
Оценка:
Здравствуйте, IT, Вы писали:

IT>Такое не интересно. Нужна семантика в рамках линка без строковых констант.

IT>Тоже самое, интересен DELETE по условию, а не для одного объекта.

ясно, просто ненавижу как это сделано в Linq2Sql
т.е.

var p = DbContext.Person.Where(_ => _.Id = myIdToDelete);
DbContext.DeleteOnSubmit(p);
DbContext.SubmitChanges();


слишком много кода, также и для Update.
хочется одной строкой

H>>А, я не особо искал, но может где-то внутри BL есть функцинальность для определения сущностей БД, т.е. список таблиц, список столбцов,..., ведь для рахных БД это по разному?


IT>Не совсем понял вопроса.

нужен функционал который бы выдавал список таблиц, список столбцов из таблицы, primary keys, ... который бы работал прозрачно для разных DataProviders.

IT>MIT. Можешь исходники хоть упаковывать в коробку и продавать.

отлично
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
The life is relative and reversible.
Re[9]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 04.01.10 19:27
Оценка:
Здравствуйте, Holms, Вы писали:

IT>>Не совсем понял вопроса.

H>нужен функционал который бы выдавал список таблиц, список столбцов из таблицы, primary keys, ... который бы работал прозрачно для разных DataProviders.

Такого нет. Но вроде как DataProviders умеют возвращать схему базы и без BLT.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: [ANN] Emit Mapper
От: Holms США  
Дата: 04.01.10 19:41
Оценка:
Здравствуйте, IT, Вы писали:

IT>Такого нет. Но вроде как DataProviders умеют возвращать схему базы и без BLT.

так это я знаю
подумалось ведь ты всёравно для разных провайдеров по своему составляешь запросы, может и эта функциональность (db schema info) где-то потребовалась и уже есть, что-бы мне не писать заново велосипед
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
The life is relative and reversible.
Re[11]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 04.01.10 21:54
Оценка:
Здравствуйте, Holms, Вы писали:

IT>>Такого нет. Но вроде как DataProviders умеют возвращать схему базы и без BLT.

H>так это я знаю
H>подумалось ведь ты всёравно для разных провайдеров по своему составляешь запросы, может и эта функциональность (db schema info) где-то потребовалась и уже есть,

Мне это совсем не нужно. Всё описание содержится в модели данных.

H>что-бы мне не писать заново велосипед


Напиши, если получится что-то всем полезное, но можно будет в BLT добавить.
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: [ANN] Emit Mapper
От: mrTwister Россия  
Дата: 04.01.10 23:34
Оценка:
Здравствуйте, IT, Вы писали:

IT>Сделать хотя бы для дефолтных мапперов. Судя по опыту они используются в 99% случаев.


Мысль здравая

IT>Именно поэтому легче делать маппинг object to object вручную.


Вовсе нет. Ты верно выше отметил, что в 99% случаев работает дефолтная конфигурация. Проще вручную бороться с 1% чем с 100%.

IT>Нужно было не лениться, а продемонстрировать.


Ну дак я и продемонстрировал здесь
Автор: mrTwister
Дата: 19.11.09


IT>А ещё лучше, чтобы основные сценарии были в библиотеке по умполчанию.


Да, наверное ты все-таки прав.

IT>Либо умолчания настраивались для всего приложения, а не для каждого сценария.


А это уже доступно. Есть дефолтный конфигуратор — класс DefaultMapConfig. Этот класс инкапсулирует в себе все правила мэппинга. Ты можешь создать один или несколько глобальных экземпляров этого класса и использовать их когда надо:

public class Program
{
    public static IMappingConfigurator config1;
    public static IMappingConfigurator config2;

    static Program()
    {
        config1 = new DefaultMapConfig()
            .MatchMembers((m1, m2) => m1.ToLower() == m2.ToLower()) // ignore case of field names
            .NullSubstitution<object, int>(state => 42) // all nulls will be int 42
            .SetConfigName("config1");

        config2 = new DefaultMapConfig()
            .NullSubstitution<object, object>(state => DBNull.Value) // all nulls will be DBNull.Value
            .SetConfigName("config2");
    }
...
    public void Foo()
    {
        ...
        var dest = ObjectsMapperManager.DefaultInstance.GetMapper<Source, Destination>(config1).Map(source);
    }
...
}


Кроме того, существует глобальная дефолтная конфигурация, которая используется по-умолчанию. Эта конфигурация доступна через "DefaultMapConfig.Instance" и её так же можно менять и соответственно поведение будет глобально меняться.

DefaultMapConfig.Instance.MatchMembers((m1, m2) => m1.ToLower() == m2.ToLower());
...
dest = ObjectsMapperManager.DefaultInstance.GetMapper<Source, Destination>().Map(source);

IT>Как я уже сказал, проблема в том, что при уж очень заумных сценариях object to object проще написать ручками.

До поры до времени — пока полей в классах не много.

IT>Но в принципе, я не возражаю. Возможностей меньше. Хотя больше пока никому не было нужно.


Потому что при работе с базой оно действительно не особо и надо.

IT>А сколько их всего O2O?


Из популярных — это AutoMapper, Glue, Otis. Есть еще и другие, сходу не вспомню.

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

IT>Проще. Поверь мне в сценариях O2O проще. Автоматика может привести к большим проблемам как я уже сказал.

А подробней про проблемы можно?

IT>Не надо передёргивать. Тестирование в одинаковых условиях нужно для того, чтобы увидеть реальную разницу и понять проблемы. Тестирование в подогнанных под один инструмент условиях (как, например, на ORMBattle) нужно для того, чтобы показать кто круче. Чувствуешь разницу?


Ок, будем считать, что я имел ввиду условия О2О, когда схемы данных известны заранее. В условиях когда схемы данных заранее неизвестны или в условиях DB2O выигрыш уже гораздо меньше. Так нормально?

IT>Это я понял. С другой стороны ты утвержаешь следующее:


IT>

IT>2) Очень гибкая. Проецировать можно что угодно на что угодно. Например, датаридер на объекты, объекты на SQL команды (UPDATE, INSERT), объекты на строку (сериализация), строку на объекты (десериализация) и так далее.


Смысл этой фразы в том, что с помощью ЕМ можно автоматически проецировать что угодно куда угодно, а не то, что ЕМ — это ORM фреймворк. Я на http://emitmapper.codeplex.com прямо так и написал:

Essential point of the source code above is not that it is another one "Super ORM". Essential point is that you can yourself develop very effective and light database access utilities in respect to your project requirements. Sometimes it is more preferable than using some huge generic ORM.


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


Подстройка маппера требуется очень редко.


T>>EM поддерживает NullSubstitution и с помощью следующей конфигурации можно создать мэппер, который вместо null будет писать 42:

T>>
T>>var mapper = ObjectsMapperManager.DefaultInstance.GetMapper<B2, A2>(
T>>        new DefaultMapConfig().NullSubstitution<decimal?,int>( state => 42 )
T>>    );
T>>

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

IT>Не падает. Только очень многословно получается. Мне это нужно при каждом маппинге выписывать или можно задать где-то одно правило на все варианты использования в программе?


Можно (см выше).

IT>Я уже хотел было обрадоваться. К сожелению, если написать два теста вот с такими командами:


IT>
IT>cmd.CommandText = "SELECT * FROM Customers";        // Test1
IT>cmd.CommandText = "SELECT Phone, * FROM Customers"; // Test2
IT>

IT>то после вызова первого теста второй отработает неправильно.

Ой
Пофиксено.

IT>Кстати, не думал в реализации GetValuesGetter попробовать убрать инициализацию fieldNum через замену делегата на лету. Будет, конечно, дополнительный косвенный вызов, но если кроме fieldNum ещё и тип поля подсмотреть, то можно будет вызывать не reader.GetValue, а, например, reder.GetInt32 и полностью избавиться от боксинга.


Если на лету менять, то боюсь проблемы с многопоточностью будут.

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


Ладно, согласен, отмазка не прошла.

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


IT>Во-вторых, в данном примере происходит двойной маппинг, что уже настолько сажает быстродействие, что о нём можно больше не говорить.


А Linq2SQL и EF именно так сейчас чаще всего и используют, они же не поддерживают POCO. Даже статьи на эту тему пишут:http://habrahabr.ru/blogs/net/71820/

IT>В-третих, твои пользователи первым делом засунут сюда анонимные типы, а с ними EM как я понял не работает совсем.

Работает без проблем. Только сборку надо пометить атрибутом
[assembly: InternalsVisibleTo("EmitMapperAssembly")]
лэт ми спик фром май харт
Re[4]: [ANN] Emit Mapper
От: Holms США  
Дата: 05.01.10 06:07
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Holms, Вы писали:


H>>пост не в тему, но

H>>последняя версия BL из svn не компилится, можете поправить

Кстати, будешь еще оптимизировать Linq реализацию, а то судя по первой таблице здесь BLToolkit не слишком скоростной.

Хотя кто знает,
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
The life is relative and reversible.
Re[5]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 05.01.10 06:34
Оценка: 18 (1)
Здравствуйте, Holms, Вы писали:

H>Кстати, будешь еще оптимизировать Linq реализацию, а то судя по первой таблице здесь BLToolkit не слишком скоростной.


Первая таблица — это не скорость. Это полнота поддержки Linq. Скорость во второй таблице. Это всё результаты на начало ноября. На сегодняшний день они выглядят следующим образом:

LINQ tests:
  Testing: BLToolkit (BLT)
  Testing: ADO.NET Entity Framework (EF)
  Testing: DataObjects.Net (DO)
  Testing: LightSpeed (LS)
  Testing: LINQ to SQL (L2S)
  Testing: NHibernate (NH)
  Testing: OpenAccess (OA)

LINQ tests scorecard:
                                       BLT        EF        DO        LS       L2S        NH        OA   Maximum      Unit
LINQ Implementation:            
  Aggregates                             3         0         0         3         0         3         2         5       f/a
  All/Any/Contains                       5         3         0         6         1       4/2         4         6       f/a
  Complex                                6         1         0         6         0         6         5         6       f/a
  Element operations                     5         4         0         6         2         6         5         9       f/a
  Filtering                              0       4/2         0       5/5       2/2       6/1         2        12       f/a
  Grouping                               2         1         0        10         1      10/2         5        10       f/a
  Join                                   2         1         0         4         0         4         2         4       f/a
  Ordering                               0       2/1         0       5/1         2         6         3         8       f/a
  Projections                          3/1       3/1       3/3       9/2       2/1       6/1         3        13       f/a
  References                             1         0         0         4         0         3         1         4       f/a
  Set operations                         0         0         0       5/1         0       6/2       4/1         9       f/a
  Standard functions                     0        12         2        12       3/1        20       9/1        25       f/a
  Take/Skip                              1         1         0       2/1         0       2/1         2         5       f/a
  Type casts                             0         1         0       3/1         1         4         2         5       f/a
Total:                          
  Performed                            121       121       121       121       121       121       121       121         #
  Passed                                93        88       116        41       107        35        72       121         #
  Failed                                28        33         5        80        14        86        49       121         #
    Properly                            27        29         2        69        10        77        47       121         #
    Asserted                             1         4         3        11         4         9         2       121         #
  Score                               76.9      72.7      95.9      33.9      88.4      28.9      59.5     100.0         %

Units:
  f/a: total count of failed tests [ / count of tests failed with assertion ],
       less is better (0 is ideal);
  #:   count;
  %:   percentage (% of passed tests), more is better.

Т.е. с 40% мы продвинулись до 76% и занимаем третье место после DO и L2S. Работы по улучшению поддержки ведутся. С DO скорее всего созтязаться смысла нет, т.к. там комплексные тесты заточены в основном на тяжёлые ORM, но может и это сделаем.

Что касается скорости linq (вторая таблица), то хотя BLT и на первом месте, но некоторые идеи по улучшению производительности есть. Мы их, кстати, в этом топике коснулись.
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: [ANN] Emit Mapper
От: Holms США  
Дата: 05.01.10 06:40
Оценка:
Здравствуйте, IT, Вы писали:


IT>Что касается скорости linq (вторая таблица), то хотя BLT и на первом месте, но некоторые идеи по улучшению производительности есть. Мы их, кстати, в этом топике коснулись.


Отлично, еще один повод переходить на BL
... << RSDN@Home 1.2.0 alpha 4 rev. 1253>>
The life is relative and reversible.
Re[4]: [ANN] Emit Mapper
От: Dog  
Дата: 05.01.10 10:04
Оценка:
T>>Каждая из стратегий имеет свои достоинства и недостатки, соответственно исходить надо из задачи. Задача Object2Object mapping — это задача с очень смутными и неопределенными вариантами использования из-за того, что их очень много.
IT>Именно поэтому легче делать маппинг object to object вручную.
Не надо смутных и неопределённых вариантов. Надо 1 простой для 90% случаев.

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

T>>Смотря на каких сценариях. Если это сценарии связанные с БД, то скорее всего да, ведь он именно на эти сценарии и заточен. Если же рассматривать сценарии Object2Object, то уже вряд ли, так как БЛТ сильно уступает конкурентам по функционалу и количеству покрываемых вариантов использования.
IT>Как я уже сказал, проблема в том, что при уж очень заумных сценариях object to object проще написать ручками. Но в принципе, я не возражаю. Возможностей меньше. Хотя больше пока никому не было нужно.
Re[2]: Маппинг списков
Автор: Dog
Дата: 18.12.09


T>>Ну, это уже "закат солнца вручную". Если уж нет автоматики, не проще ли тогда вообще написать:

IT>Проще. Поверь мне в сценариях O2O проще. Автоматика может привести к большим проблемам как я уже сказал. Да и автоматику на всё не сделаешь. Обычно маппинг во вложенные объекты нужно делать из плоских стркутур. Поле BillingAddressCity в BillingAddress.City. И всё, уже пошёл закат солнца.
Не надо на всё. Напиши в доке, то-то и то-то не поддерживает. Но чтобы User.Bank.Address в User.Bank.Address или вот
Автор: Dog
Дата: 17.12.09
замапило без приседаний.
Re[5]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 05.01.10 17:33
Оценка:
Здравствуйте, mrTwister, Вы писали:

IT>>Либо умолчания настраивались для всего приложения, а не для каждого сценария.


T>Кроме того, существует глобальная дефолтная конфигурация, которая используется по-умолчанию. Эта конфигурация доступна через "DefaultMapConfig.Instance" и её так же можно менять и соответственно поведение будет глобально меняться.


Кстати, если по религиозным соображениям не хочется делать автоматический маппинг nullable значений в обычные, то для таких конфигураций можно было бы сделать пару методов, которые конфигурировали бы все необходимые типы. По опыту в подавляющем большинстве случаев для замены нулевых значений используются только два значения: 0 или MinValue. Пара методов SetNullToZero и SetNullToMinValue сняли бы большинство вопросов. Плюс неплохо было бы сделать такое конфигурируемым через app.config.

IT>>Проще. Поверь мне в сценариях O2O проще. Автоматика может привести к большим проблемам как я уже сказал.

T>А подробней про проблемы можно?

Например, что делать с circle references, как обрабатывать наследование, кто должен создавать и должен ли экземпляры вложенных объектов, должны ли эти экземпляры копироваться по ссылке или создаваться и маппится по-новой.

На все эти вопросы есть либо ни одного, либо два-три ответа, которые валидны в дефолтной конфигурации.

IT>>Я уже хотел было обрадоваться. К сожелению, если написать два теста вот с такими командами:


IT>>
IT>>cmd.CommandText = "SELECT * FROM Customers";        // Test1
IT>>cmd.CommandText = "SELECT Phone, * FROM Customers"; // Test2
IT>>

IT>>то после вызова первого теста второй отработает неправильно.

T>Ой

T>Пофиксено.

Вижу. Производительность сразу просела. Стало даже медленней чем было по именам.

IT>>Кстати, не думал в реализации GetValuesGetter попробовать убрать инициализацию fieldNum через замену делегата на лету. Будет, конечно, дополнительный косвенный вызов, но если кроме fieldNum ещё и тип поля подсмотреть, то можно будет вызывать не reader.GetValue, а, например, reder.GetInt32 и полностью избавиться от боксинга.


T>Если на лету менять, то боюсь проблемы с многопоточностью будут.


Какие проблемы? Два потока одновременно проинициализируют одно и тоже поле? Так это ерунда.

IT>>Во-вторых, в данном примере происходит двойной маппинг, что уже настолько сажает быстродействие, что о нём можно больше не говорить.

T>А Linq2SQL и EF именно так сейчас чаще всего и используют, они же не поддерживают POCO. Даже статьи на эту тему пишут:http://habrahabr.ru/blogs/net/71820/

Не нашёл где там про это пишут.

IT>>В-третих, твои пользователи первым делом засунут сюда анонимные типы, а с ними EM как я понял не работает совсем.

T>Работает без проблем. Только сборку надо пометить атрибутом
T>[assembly: InternalsVisibleTo("EmitMapperAssembly")]

Нужно тогда диагностику подправить, пользователи замучают вопросами.
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 05.01.10 18:18
Оценка:
Здравствуйте, Dog, Вы писали:

IT>>Именно поэтому легче делать маппинг object to object вручную.

Dog>Не надо смутных и неопределённых вариантов. Надо 1 простой для 90% случаев.

Для 90% случаев нужно строить граф объектов или не нужно?

IT>>Как я уже сказал, проблема в том, что при уж очень заумных сценариях object to object проще написать ручками. Но в принципе, я не возражаю. Возможностей меньше. Хотя больше пока никому не было нужно.

Dog>Re[2]: Маппинг списков
Автор: Dog
Дата: 18.12.09


Что делать в твоём примере, если в A есть обратная ссылка на Foo?

IT>>Проще. Поверь мне в сценариях O2O проще. Автоматика может привести к большим проблемам как я уже сказал. Да и автоматику на всё не сделаешь. Обычно маппинг во вложенные объекты нужно делать из плоских стркутур. Поле BillingAddressCity в BillingAddress.City. И всё, уже пошёл закат солнца.

Dog>Не надо на всё. Напиши в доке, то-то и то-то не поддерживает. Но чтобы User.Bank.Address в User.Bank.Address или вот
Автор: Dog
Дата: 17.12.09
замапило без приседаний.


Можно, конечно, сделать. Но это уже будет совсем другой маппинг с другими правилами, больше похожий на клонирование.
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: [ANN] Emit Mapper
От: Dog  
Дата: 05.01.10 19:06
Оценка:
IT>>>Как я уже сказал, проблема в том, что при уж очень заумных сценариях object to object проще написать ручками. Но в принципе, я не возражаю. Возможностей меньше. Хотя больше пока никому не было нужно.
Dog>>Re[2]: Маппинг списков
Автор: Dog
Дата: 18.12.09

IT>Что делать в твоём примере, если в A есть обратная ссылка на Foo?
Нету ни циклических ни обратных...

Dog>>Не надо на всё. Напиши в доке, то-то и то-то не поддерживает. Но чтобы User.Bank.Address в User.Bank.Address или вот
Автор: Dog
Дата: 17.12.09
замапило без приседаний.

IT>Можно, конечно, сделать. Но это уже будет совсем другой маппинг с другими правилами, больше похожий на клонирование.
Да. Вот что-то подобное надо.
... а что за другие правила ? Этих что есть хватает вроде, главное обойти и посоздавать всё акуратно.
Re[6]: [ANN] Emit Mapper
От: mrTwister Россия  
Дата: 05.01.10 20:15
Оценка:
Здравствуйте, IT, Вы писали:

IT>Например, что делать с circle references,

А как пользователь будет вручную писать маппинг, учитывающий circle references? Он тоже будет граф объектов строить? Сомневаюсь. Вот и мэппер пусть делает также. Это ведь не серебряная пуля, а инструмент, позволяющий иногда обходиться без ручного кодирования — не более того.

IT>как обрабатывать наследование,


А какие проблемы с наследованием?

IT>кто должен создавать и должен ли экземпляры вложенных объектов,


ЕМ работает так: по-умолчанию использует дефолтный конструктор. Но если надо, можно задать кастомный:

var mapper = ObjectsMapperManager.DefaultInstance.GetMapper<Source, Destination>(
        new DefaultMapConfig().ConstructBy<Foo>( () => new FooDescendant("hello!") )
    );


IT>должны ли эти экземпляры копироваться по ссылке или создаваться и маппится по-новой.


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

IT>На все эти вопросы есть либо ни одного, либо два-три ответа, которые валидны в дефолтной конфигурации.


Надо лишь дать пользователю возможность ответить на эти вопросы и далее делать все, как он скажет.

T>>Ой

T>>Пофиксено.

IT>Вижу. Производительность сразу просела. Стало даже медленней чем было по именам.


Странно, у меня не практически просела и выше чем была с именами и чем у БЛТ также.

IT>>>Кстати, не думал в реализации GetValuesGetter попробовать убрать инициализацию fieldNum через замену делегата на лету. Будет, конечно, дополнительный косвенный вызов, но если кроме fieldNum ещё и тип поля подсмотреть, то можно будет вызывать не reader.GetValue, а, например, reder.GetInt32 и полностью избавиться от боксинга.


T>>Если на лету менять, то боюсь проблемы с многопоточностью будут.


IT>Какие проблемы? Два потока одновременно проинициализируют одно и тоже поле? Так это ерунда.


А если два потока будут одновременно использовать один мэппер (для одного и того же класса), но работать будут с ридерами разной структуры (разный порядок и тип полей)?
Вообще, как бы там ни было я это поборол путем дифференциации датаридеров. Пользователь может либо сам указать идентификатор ридера (например, в качестве идентификатора можно использовать сам запрос) — это работает чуть быстрей, либо идентификатор может быть построен автоматически на основании имен и типов полей — это чуть медленнее. Также сделал прямой вызов методов типа "GetInt32()" как ты предложил, что на синтетическом тесте дало еще буст в 25% (то есть у БЛТ тоже есть куда расти). Тест:

CREATE TABLE [dbo].[test](
    [col1] [int] NOT NULL,
    [col2] [int] NOT NULL,
    [col3] [int] NOT NULL,
    [col4] [int] NOT NULL,
    [col5] [int] NOT NULL,
    [col6] [int] NOT NULL
)
GO

DECLARE @i INT
SET @i = 0
while (@i < 100)
BEGIN
    INSERT INTO test 
    VALUES
    (
        RAND() * 100, RAND() * 100, RAND() * 100, RAND() * 100, RAND() * 100,  RAND() * 100
    )
    SET @i = @i + 1
END
GO

public class test
{
    public int col1;
    public int col2;
    public int col3;
    public int col4;
    public int col5;
    public int col6;
}


IT>Не нашёл где там про это пишут.

Ну там говориться в том числе об использовании DTO для передачи данных между слоями приложения. Очень часто объекты Linq2Sql или EF стараются не выставлять наружу из уровня доступа к данным, чтобы не тянуть зависимость к ORM на все слои, которые общаются с Data Access Layer'ом. То есть DAL имеет интерфейс на основе DTO — POCO объектов, а внутри он уже мэппит их на соответствующие объекты ORM. Дело в том, что обычно этот мэппинг пишут вручную и это довольно геморройно.
лэт ми спик фром май харт
Re[7]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 06.01.10 14:34
Оценка:
Здравствуйте, Dog, Вы писали:

IT>>Что делать в твоём примере, если в A есть обратная ссылка на Foo?

Dog>Нету ни циклических ни обратных...

А если завтра понадобится?

IT>>Можно, конечно, сделать. Но это уже будет совсем другой маппинг с другими правилами, больше похожий на клонирование.

Dog>Да. Вот что-то подобное надо.
Dog>... а что за другие правила ? Этих что есть хватает вроде, главное обойти и посоздавать всё акуратно.

Вот как раз что-то подобное клонированию сделать универсально не так просто.
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: [ANN] Emit Mapper
От: IT Россия linq2db.com
Дата: 06.01.10 15:19
Оценка:
Здравствуйте, mrTwister, Вы писали:

IT>>Например, что делать с circle references,

T>А как пользователь будет вручную писать маппинг, учитывающий circle references? Он тоже будет граф объектов строить? Сомневаюсь.

Не надо сомневаться. Я вчера весь вечер убил, чтобы разрулить обратные ссылки. Сегодня буду переписывать всё на двойной проход по дереву, т.к. в один проход задача у меня не решилась. Там, конечно, не совсем просто маппинг, но задача схожая.

T>Вот и мэппер пусть делает также. Это ведь не серебряная пуля, а инструмент, позволяющий иногда обходиться без ручного кодирования — не более того.


Так и обратные ссылки это инструмент. Лучше бы, конечно, без них, но не всегда это получается.

IT>>как обрабатывать наследование,

T>А какие проблемы с наследованием?

В EM ты создаёшь маппер по типам источника и приёмника. Если в качестве источника будет не тип, который был при инициализации маппера, а его наследник, то всё будет пучком или возникнут проблемы?

IT>>кто должен создавать и должен ли экземпляры вложенных объектов,

T>ЕМ работает так: по-умолчанию использует дефолтный конструктор. Но если надо, можно задать кастомный:

А если моя логика подразумевает, что если в целевом объекте ссылка нулевая, то не нужно создавать по ней объекта?

IT>>Какие проблемы? Два потока одновременно проинициализируют одно и тоже поле? Так это ерунда.

T>А если два потока будут одновременно использовать один мэппер (для одного и того же класса), но работать будут с ридерами разной структуры (разный порядок и тип полей)?

Маппер не должен хранить в себе состояние и не будет проблем.

T>Вообще, как бы там ни было я это поборол путем дифференциации датаридеров. Пользователь может либо сам указать идентификатор ридера (например, в качестве идентификатора можно использовать сам запрос) — это работает чуть быстрей, либо идентификатор может быть построен автоматически на основании имен и типов полей — это чуть медленнее. Также сделал прямой вызов методов типа "GetInt32()" как ты предложил, что на синтетическом тесте дало еще буст в 25% (то есть у БЛТ тоже есть куда расти).


Расти всегда есть куда. 25% это очень не плохо. Если раньше я только раздумывал, то теперь точно займусь этим для линка. Спасибо за проверку идеи

IT>>Не нашёл где там про это пишут.

T>Ну там говориться в том числе об использовании DTO для передачи данных между слоями приложения. Очень часто объекты Linq2Sql или EF стараются не выставлять наружу из уровня доступа к данным, чтобы не тянуть зависимость к ORM на все слои, которые общаются с Data Access Layer'ом. То есть DAL имеет интерфейс на основе DTO — POCO объектов, а внутри он уже мэппит их на соответствующие объекты ORM. Дело в том, что обычно этот мэппинг пишут вручную и это довольно геморройно.

Это чисто дизайнерские решения.
Если нам не помогут, то мы тоже никого не пощадим.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.