Здравствуйте, 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'ы для вложенных типы получились такие:
Всё должно быть максимально очевидно и интуитивно понятно.
Все членомаперы должны быть доступны для IEnumerate'ния сразу по созданию мапера.
Вложенные типы должны учитываться в CRUDL операциях и в вызове хранимок из датаакцесоров.
Вложенные типы могут быть структурами, классами и абстрактными классами. (Тут всё почти ок, об этом дальше.)
Самое простое решение это тупо создавать комплексные маперы и не добавлять префикс. Первое у меня сомнений не вызывает, а вот со вторым всё не так и не просто.
Как минимум, нужно учитывать совместимость с предыдущими версиями. Кроме того, префикс не даст наступить на грабли с одинаковыми именами. В тоже время точку трудно использовать в имени параметра спрока. Да и в имени поля записи ей тоже не место. Вобщем
Могу предложить компромисный вариант: генерацию префикса контролировать атрибутом, и по умолчанию сделать его вкыл. Вот так
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>>