Вышла новая версия Rsdn.Framework.Data. Впрочем, это уже не совсем RFD. Вторая версия полностью переработана, включая само название библиотеки Новое название – Business Logic Toolkit (BLToolkit) призвано более точно отразить расширенные возможности библиотеки и её назначение.
Ниже приведён неполный список возможностей библиотеки:
Класс DbManager – высокоуровневая, независимая от провайдера данных обёртка над ADO.NET, дополняющая её такими методами как ExecuteObject, ExecuteList, ExecuteDictionary и многими другими.
Класс DataAccessor – базовый класс для реализации слоя доступа к данным (DAL), предлагающий набор базовых CRUDL операций плюс генератор вызова сохранённых процедур на основе сигнатуры абстрактных методов.
Универсальный маппер (BLToolkit.Mapper) – расширяемый набор классов, позволяющий отображать друг в друга, помимо классического отображения данных из БД в объекты, практически любые другие структуры данных. Стандартный набор мапперов включает поддержку следующих структур данных: DataReader, DataRow (DataRowView, DataTable, DataSet), IDictionary, IList, бизнес объекты. EditableObjects – базовые классы для построения иерархий бизнес объектов, способных уведомлять потребителей об изменении своего внутреннего состояния и поддерживающих методы AcceptChanges, RejectChanges и флаг IsDirty. Validation – набор классов и атрибутов для реализации простого в использовании механизма валидации бизнес объектов, основанного на применении атрибутов. ObjectBinder – компонент для баиндинга бизнес объектов. Кроме него в namespace BLToolkit.ComponentModel входят классы, предлагающие базовую реализацию интерфейсов ITypedList, IBindingList. TypeAccessor – создание объектов и быстрый доступ к полям и свойствам классов в обход технологии Reflection. Ранее эта возможность была частично реализована в классе MapDescriptor. Теперь доступ к классу и сам маппинг разделены. Кроме прочего класс TypeAccessor используется для создания экземпляров объектов сгенерированных классов. EmitHelper – emit с человеческим лицом. Обёртка над ILGenerator, предлагающая типизированные версии метода ILGenerator.Emit. TypeBuilder – расширяемый генератор классов.
Игорь, в этом архиве не хватает файла
bltoolkit_dev\Source\Data\ParameterReader.cs
IT>Вышла новая версия Rsdn.Framework.Data. Впрочем, это уже не совсем RFD. Вторая версия полностью [...] IT>Исходные коды проекты доступны здесь.
Рад стараться
Сим>А документация к продукту планируется? :-P
Вот думаю в каком виде её лучше делать. Опыт использования XML комментариев показал, что это удобно разве что только для контекстной всплывающей подсказки. Но не для документации. Может быть лучше сделать набор небольших статеек и howto с примерами использования?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Сим, Вы писали:
Сим>>Супер!!!
IT>Рад стараться
Сим>>А документация к продукту планируется? :-P
IT>Вот думаю в каком виде её лучше делать. Опыт использования XML комментариев показал, что это удобно разве что только для контекстной всплывающей подсказки. Но не для документации. Может быть лучше сделать набор небольших статеек и howto с примерами использования?
ИМХО наилучший вариант. Примерно как это было сделано к первому RFD.
XML комментарии только дополнят картину
Возможно ли использование составных первичных ключей, например [GUID и ID] вместе (ввести свой класс ключа) и их маппинг на параметры (надеюсь понятно высказался)?
Каким образом реализуется маппинг на хп. динамическая сборка, рефлексия...?
Как я понимаю, раз реализованы IEditableObject и IBindingList значит теперь мы имеем полноценную возможность биндинга бизнес-объектов в WinForms или это не совсем так?
Есть ли собственный механизм сериализации у базовых классов бизнес-объектов?
т.е. сериализация коллекций и всего графа бизнес-объекта была задача не для слабонервных...
Есть ли возможность отложенной загрузки свойства бизнес-объекта?
Здравствуйте, IT, Вы писали:
Сим>>А документация к продукту планируется? :-P IT>Вот думаю в каком виде её лучше делать. Опыт использования XML комментариев показал, что это удобно разве что только для контекстной всплывающей подсказки. Но не для документации. Может быть лучше сделать набор небольших статеек и howto с примерами использования?
Один из вариантов — это развернуто прокомментировать основные классы и нэймспейсы (как раз в виде статеек и howto с примерами) прямо в коде в XML. Тогда вся нужная инфа автоматически хранится в одной chm'ке. Имхо, это гораздо удобнее, чем открывать отдельные статьи и доки.
Здравствуйте, снежок, Вы писали:
С>Возможно ли использование составных первичных ключей, например [GUID и ID] вместе (ввести свой класс ключа) и их маппинг на параметры (надеюсь понятно высказался)?
Маппер и DbManager вообще никак не оперируют ключами. Им это не нужно. DataAccessor для генерации SQL поддерживает многосегментные ключи. Размечать их можно атрибутом PrimaryKeyAttribute.
С>Каким образом реализуется маппинг на хп. динамическая сборка, рефлексия...?
Для маппинга генерируется специальный код, который позволяет устранить проблемы производительности reflection.
С>Как я понимаю, раз реализованы IEditableObject и IBindingList значит теперь мы имеем полноценную возможность биндинга бизнес-объектов в WinForms или это не совсем так?
Это так. Уже сейчас можно баиндится не только на свойства, но и на публичные поля объекта. В дальнейшем можно будет баиндиться на поля и свойства вложенных объектов (это уже сделано в RFD, нужно только перенести). В планах баиндинг одновременно на бизнес объект и его UI-представитель.
Вообще, применение баиндига на бизнес объекты в моём текущем проекте меня даже несколько озадачило (в хорошем смысле). Я не понимаю почему в MS не довёли эту технологию до ума. С небольшой доработкой контролов — это реальный путь к решению проблемы повторного использования визуальных компонент. Даже такие вещи как Enable/Disable, Hide/Show определённых полей на форме делается элементарно с помощью баиндига. Т.е. фактически из формы можно убрать большую часть UI логики в отдельные компоненты, которые в дальнейшем можно свободно повторно использовать.
С>Есть ли собственный механизм сериализации у базовых классов бизнес-объектов? С>т.е. сериализация коллекций и всего графа бизнес-объекта была задача не для слабонервных...
Нет. Такого похоже нет, но при внятной постановке вопроса думаю это можно решить.
С>Есть ли возможность отложенной загрузки свойства бизнес-объекта?
Нет. Но подобные вещи несложно делаются расширениями библиотеки. В саму библиотеку это не было внесено по причине специфики подобных вещей в каждом конкретном случае.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
С>>Есть ли собственный механизм сериализации у базовых классов бизнес-объектов? С>>т.е. сериализация коллекций и всего графа бизнес-объекта была задача не для слабонервных...
IT>Нет. Такого похоже нет, но при внятной постановке вопроса думаю это можно решить.
А что, в BLToolkit появился базовый класс для бизнес-объектов?
Я пока не смотрел еще, ивините, думал, что единственным ограничение — уровень доступа public.
А сериализация графа делается достаточно просто (спасибо Inside .Net Remoting и BinaryFormatter'у). Я когда-то это делал, и была только одна проблема — сериализация ondemand-данных. Иногда их нужно было вытаскивать из базы (если мы отдаем сериализованные данные кому-то налево), а иногда не нужно (когда сериализуем в Asp.Set outofprocess Session. Тоже легко решилась (три сторочки кода(за три дня)
Если кому-то надо, покажу.
Здравствуйте, Пух, Вы писали:
Пух>А что, в BLToolkit появился базовый класс для бизнес-объектов? Пух>Я пока не смотрел еще, ивините, думал, что единственным ограничение — уровень доступа public.
Базовый класс только для EditableObjects. Это как бы отдельная часть библиотеки. Мапперу по прежнему всё равно что куда перекладывать.
Пух>Если кому-то надо, покажу.
Покажи, может кому и сгодится, а если сильно понравится, то можно будет сделать поддержку в библиотеке.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Покажи, может кому и сгодится, а если сильно понравится, то можно будет сделать поддержку в библиотеке.
Че-то слишком много получилось Если что, весь текст можно пропустить. В тексте описаны проблемы, а решения в коде.
Как сказал снежок, сериализация коллекций и всего графа бизнес-объектов — задача не для слабонервных. И, так как BinaryFormatter прекрасно с этой задачей справляется, наиболее простой способ обеспечения сериализации наших объектов — это их настройка и подготовка к тому, чтобы BinaryFormatter мог их правильно сериализовать и десериализовать.
Для начала нужно наши классы атрибутом Serializable, а поля, которые никогда не сериализуются, пометим атрибутом NonSerialized. В простых случаях этого достаточно, чтобы граф мог правильно сериализоавться/десериализоваться. Но на практике приходится помнить о трех моментах (а может, их и больше).
1) Если мы используем загрузку данных по требованию, и к началу сериализации данные еще не загружены, то они и не будут сериализоавны, потому что их нет.
Иногда такое поведение нас устраивает. Например, в ASP.NET приложении мы используем SessionStateMode отличный от InProc. В этом случае, при помещении наших объектов в сессию, они будут сериализованы. И то, что при этом не происходит загрузки данных по требованию, очень хорошо.
В других случаях мы хотим перед сериализацией потребовать загрузку данных. Например, мы сериализуем объекты для передачи в какую-нибудь внешнюю систему, которой не понравится, что значения половины свойств и коллекций раны null.
Т.е., наши объекты должны иметь возможность выполнять какие-то действия до начала сериализации. При этом они должны знать, зачем их собираются сериализовать.
Более точно, у базового класса должен быть метод:
Наследники будут его переопределять и, в зависимости от контекста, готовить себя к сериализации.
2) Второй момент, о котором нужно помнить — делегаты тоже сериализуются и десериализуются.
Это значит, что если наши классы поддерживают события, то при сериализации бизнес-объекта все подписчики на его события тоже будут сериализованы (если удастся их сереализовать). Хорошо, если подписчиками являются наши же бизнес-объекты. А если какой-нибудь элемент UI подпишется на событие PropertyChanged нашего объекта, нам врядли понравится, что при сериализации нашего обекта мы потянем за собой UI. Тем более, если он не поддерживает сериализацию. В общем, нам нужно "скинуть" внешних подписчиков перед сериализацией, и вернуть все на место сразу после сериализации.
Т.е., наши объекты должны иметь возможность выполнять какие-то действия не только до начала сериализации, но и после ее завершения.
Значит, будет еще один метод.
Как сделать так, чтобы эти методы вызывались в нужное время.
В FW 1.1 для это нужно реализовать интерфейс ISerializable:
public void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
OnSerializing(context);
MemberInfo[] fields = FormatterServices.GetSerializableMembers(this.GetType());
object[] values = FormatterServices.GetObjectData(this, fields);
int count = fields.Length;
for (int i = 0; i < count; i++)
{
info.AddValue(fields[i].Name, values[i]);
}
OnSerialized(context);
}
protected EditableObject(SerializationInfo info, StreamingContext context)
{
MemberInfo[] fields = FormatterServices.GetSerializableMembers(this.GetType());
object[] values = FormatterServices.GetObjectData(this, fields);
SerializationInfoEnumerator enumerator = info.GetEnumerator();
int i = 0;
while (enumerator.MoveNext())
{
if (fields[i].Name == enumerator.Name
&& enumerator.Value != null)
{
if (!(enumerator.Value is IConvertible))
{
values[i] = enumerator.Value;
}
else
{
FieldInfo field = (FieldInfo)fields[i];
values[i] = Convert.ChangeType(enumerator.Value, field.FieldType);
}
i++;
}
}
FormatterServices.PopulateObjectMembers(this, fields, values);
}
Тут у нас, якобы, кастомная сериализация, но на самом деле мы ничего особенного не делаем.
BinaryFormatter сделал бы то же самое. А весь этот код написан только для того, чтобы вызвать два виртуальных метода.
К счастью, FW 2.0 избавляет нас от этого предоставляя атрибуты OnSerializingAttribute и OnSerializedAttribute. Если применить их к методам, то методы будут вызваны соответственно перед сериализацией и после. Методы не могут быть виртуальными.
В итоге для EditableObject придем примерно к такому:
[ImplementInterface(typeof(IEditable))]
[ImplementInterface(typeof(IMemberwiseEditable))]
[ImplementInterface(typeof(IPrintDebugState))]
[Serializable]
public abstract class EditableObject
: IEditableObject, INotifyPropertyChanged, ISupportMapping, IPropertyChanged, INotifyObjectEdit
#if !FW2
, ISerializable
#endif
{
#region Serialization Support
// Наследники переопределят эти методы и, в зависимости от значения context,
// выполнят подкогтвку к сериализации.
// Наша собственная подготовка - это сброс и восстановление внешних подписчиков.
//protected virtual void OnSerializing(StreamingContext context)
{
DropExternalEventHandlers();
}
protected virtual void OnSerialized(StreamingContext context)
{
RestoreExternalEventHandlers();
}
#if FW2
[OnSerializing]
private void InternalOnSerializing(StreamingContext context)
{
OnSerializing(context);
}
[OnSerialized]
private void InternalOnSerialized(StreamingContext context)
{
OnSerialized(context);
}
#else
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
OnSerializing(context);
MemberInfo[] fields = FormatterServices.GetSerializableMembers(this.GetType());
object[] values = FormatterServices.GetObjectData(this, fields);
for (int i = 0; i < fields.Length; i++)
info.AddValue(fields[i].Name, values[i]);
OnSerialized(context);
}
protected EditableObject(SerializationInfo info, StreamingContext context)
{
MemberInfo[] fields = FormatterServices.GetSerializableMembers(this.GetType());
object[] values = FormatterServices.GetObjectData(this, fields);
SerializationInfoEnumerator enumerator = info.GetEnumerator();
int i = 0;
while (enumerator.MoveNext())
{
if (fields[i].Name == enumerator.Name && enumerator.Value != null)
{
if (enumerator.Value is IConvertible)
{
FieldInfo field = (FieldInfo)fields[i];
values[i] = Convert.ChangeType(enumerator.Value, field.FieldType);
}
else
{
values[i] = enumerator.Value;
}
i++;
}
}
FormatterServices.PopulateObjectMembers(this, fields, values);
}
#endif
ArrayList _propertyChangedExternalHandlers;
protected virtual void DropExternalEventHandlers()
{
if (PropertyChanged == null)
return;
_propertyChangedExternalHandlers = new ArrayList();
Delegate[] invocationList = PropertyChanged.GetInvocationList();
foreach (Delegate handler in invocationList)
{
if (!(handler.Target is EditableObject))
{
PropertyChanged -= (PropertyChangedEventHandler)handler;
_propertyChangedExternalHandlers.Add(handler);
}
}
// то же для ObjectEdit
// Может, этот код как-то обобщается, я не знаю.
}
protected virtual void RestoreExternalEventHandlers()
{
if (_propertyChangedExternalHandlers == null)
return;
foreach (PropertyChangedEventHandler handler in _propertyChangedExternalHandlers)
{
PropertyChanged += handler;
}
_propertyChangedExternalHandlers = null;
// то же для ObjectEdit
}
#endregion//
// настоящая реализация EditableObject
//
}
3) Третье, о чем нужно помнить — может так получиться, что когда мы захотим десереализоваться, динамическая сборка с нашими бизнес-классами еще не сконструирована или не загружена в домен.
Тут лучше Игорь скажет, как правильно спровоцировать загрузку сборки.
Здравствуйте, IT, Вы писали:
IT>Вышла новая версия Rsdn.Framework.Data. Впрочем, это уже не совсем RFD. Вторая версия полностью переработана, включая само название библиотеки [...] IT>Вышла новая версия Rsdn.Framework.Data. Впрочем, это уже не совсем RFD. Вторая версия полностью [...]
nice !
по этому поводу есть вопрос :
1. У класса DataView есть одно полезное свойство — RowFilter
Не подскажите как можно реализовать такой же механизм фильтрации при помощи BLToolkit ?
Здравствуйте, nitr0, Вы писали:
N>1. У класса DataView есть одно полезное свойство — RowFilter N> Не подскажите как можно реализовать такой же механизм фильтрации при помощи BLToolkit ?
Этот механизм существует в стандартном фреймворке — метод List.FindAll.
Если нам не помогут, то мы тоже никого не пощадим.