Re[2]: Вопрос по вложенным классам
От: Блудов Павел Россия  
Дата: 30.05.06 02:30
Оценка:
Здравствуйте, IT, Вы писали:

IT>Не единственное. Как такой вариант?

IT>
IT>[MapField("FirstName",  "Name.First")]
IT>[MapField("LastName",   "Name.Last")]
IT>[MapField("MiddleName", "Name.Middle")]
IT>public abstract class Person : EditableObject
IT>{
IT>


Вариант канает, но требует установки атрибутов у внешнего класса. Красота и простота теряются.
Проблема нынешней реализации вложенных классов в том, что комплексные маперы создаются по требованию. При этом нужно знать имя.
Т.е. можно легко нарваться на ситуацию, когда в момент времени А у объекта N MemberMapper'ов, а в момент B их уже N + M, т.к. поtearoffились несколько комплексных маперов. В тестах всё может и работает как часики, а в реальной жизни у честных людей полезут странные и невоспроизводимые горбухи.
Предложенный тобой способ служит для одной цели: задать комплекстные маперы заранее, чтобы они не создавались на ходу.

Тут требуются переделки в консерватории. У меня usecase'ы для вложенных типы получились такие:
Самое простое решение это тупо создавать комплексные маперы и не добавлять префикс. Первое у меня сомнений не вызывает, а вот со вторым всё не так и не просто.
Как минимум, нужно учитывать совместимость с предыдущими версиями. Кроме того, префикс не даст наступить на грабли с одинаковыми именами. В тоже время точку трудно использовать в имени параметра спрока. Да и в имени поля записи ей тоже не место. Вобщем

Могу предложить компромисный вариант: генерацию префикса контролировать атрибутом, и по умолчанию сделать его вкыл. Вот так
public abstract class RecordHeader
{
    public abstract string Last { get; set; }
    public abstract string First { get; set; }
    public abstract string Middle { get; set; }
}

public abstract class Person
{
    [MapIgnore(false), Prefix(null)]
    public abstract RecordHeader Name { get; set; } // Префикса нету; Last, First & Middle попадают в Person как родные.
    [MapIgnore(false), Prefix("Foo")]
    public abstract RecordHeader AlsoName { get; set; } // Last, First & Middle попадают в Person как Foo.Last, Foo.First & Foo.Middle
    [MapIgnore(false)]
    public abstract RecordHeader NameAgain { get; set; } // Last, First & Middle попадают в Person как NameAgain.Last, NameAgain.First & NameAgain.Middle
}

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

Теперь о багах. Вот такой тест стреляется:
        public abstract class RecordHeader
        {
            public abstract string Last { get; set; }
            public abstract string First { get; set; }
            public abstract string Middle { get; set; }
        }

        public abstract class Person
        {
            [MapIgnore(false)]
            public abstract RecordHeader Name { get; set; }
            public abstract int ID { get; set; }
        }

        [Test]
        public void InnerTest()
        {
            ObjectMapper om = Map.GetObjectMapper(typeof(Person));

            Person o = (Person)om.CreateInstance();

            om.SetValue(o, "Name.First", "Crazy");
            om.SetValue(o, "Name.Last", "Frog");

            Person p2 = Map.ObjectToObject<Person>(o);

            Assert.AreEqual(p2.Name.First, o.Name.First);
            Assert.AreEqual(p2.Name.Last, o.Name.Last);
        }


Выяснилось, что в BLToolkit.Mapping.MemberMapper.MapFrom есть вот такая строчка:
            // ...
            Type valueType  = value.GetType();
            Type memberType = mapInfo.Type;

            if (valueType != memberType)
            {
            // ...


Здесь valueType == "Mapping.MapFieldAttributeTest.BLToolkitExtension.RecordHeader",
а memberType == "Mapping.MapFieldAttributeTest+RecordHeader"
В коране
Автор(ы): Игорь Ткачёв
Дата: 01.07.2003
В статье подробно рассматривается состав и способы применения пространства имён Rsdn.Framework.Data, представляющего собой высокоуровневую обёртку над ADO.NET.
сказано, что так и должно быть, и что лечится это заменой явного сравнения на проверку ДНК. И, действительно, вылечилось заменой на
if (!memberType.IsAssignableFrom(valueType))


Но всё оказалось куда как запущеней... Дело в том, что RecordHeader это класс и при мапиньи p2 получил от o ссылку на Name Теперь если изменить
o.Name.First = "Foo"
то и
p2.Name.First станет "Foo"
Приплыли.
Здесь нужно что-то из разряда System.ComponentModel.ImmutableObject. Т.е. не скалярные типы должны или клонироваться, или для них нужно заводить мапера и мапить по полям. Может я чего-то недопонимаю? Меня смущает тот факт, что RecordHeader вообще попал в членомаперы. Этой проблемы вообще бы не случилось, если вместо членомапера для поля Name оказались три мапера для его полей.
Кстати, в твоём варианте,
IT>
IT>[MapField("FirstName",  "Name.First")]
IT>[MapField("LastName",   "Name.Last")]
IT>[MapField("MiddleName", "Name.Middle")]
IT>public abstract class Person : EditableObject
IT>{
IT>

Происходит вообще что-то подозрительное. Сначала мапится поле Name, т.е. p2.Name и o.Name указывают на один и тот же объект, затем трижды происходит складывание строк туда же, где они и находятся.

БП>>P.S. Игорь, если ты не против, но сильно занят, то гони ключи от квартиры, буду сам переделывать.

IT>Да, видимо пора.
Ты, это, в месенджере объявись. У меня paul(at)ozero.net
... << RSDN@Home 1.2.0 alpha rev. 642>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.