Работа с бизнес-объектами
Возник такой вопрос: Как пройти по всем источникам данных переданного в отчет бизнес-объекта?
Пробовал сделать так:
Пробовал сделать так:
foreach (FastReport.Data.BusinessObjectDataSource bods in report.Dictionary.DataSources)
{
//...
}
но этот метод проходит только по источникам данных "нулевого" уровня. Вложенные источники данных бизнес-объекта в этот обход не попадают.
Комментарии
Использовать так:
Учтите следующий момент: после того как вызвали report.RegisterData(bobject, "my object"), вложенные источники не создаются! Они создадутся после того, как юзер пощелкает в окне "Данные/Выбрать данные для отчета". Если нужно сразу создавать какую-то структуру с указанным уровнем вложенности, используйте вызов report.RegisterData(bobject, "my object", max_nesting_level).
возникла такая вот проблема:
Имеется бизнес-объект:
Создается экземпляр:
И создается бизнес-объект:
Далее, имеется экзепляр report объекта Report и в нем необходимо зарегистрировать данные:
(1*): Будет ли достаточно активизировать только корневые источники данных, чтобы в этом случае все вложенные источники данных также стали активными (т.е. чтобы при вызове редактора они сразу бы попали в дерево данных, и пользователю не надо было вручную их добавлять)?
(2*): Здесь необходимо синхронизировать словарь источника данных. В моем случае в передаваемом бизнес-объекте может измениться как значение DisplayName атрибутов любого свойства или класса, так и сама структура бизнес-объекта.
С проверкой того, отличается ли передаваемый бизнес-объект от зарегистрированного в отчете я вроде справился:
... ...
Т.е. источники данных которые есть в словаре отчета, но нет в передаваемом бизнес-объекте этим методом выявляются. А вот как корректно удалить их из словаря отчета я так и не понял.
И вторая задача: в бизнес-объекте могут появиться новые источники данных, их надо зарегистрировать в отчете и активировать. Получилось сделать, но только частично: для корневых бизнес-объектов вроде работает все нормально, а вот если появился новый источник данных на втором(и более) уровне вложенности бизнес-объекта, после вызова RegisterData(bobject, "Name on new source", level); новый источник не добавляется в коллекцию Columns родителя (для добавления приходится лезть в Данные ->Выбрать данные для отчета… и вручную его активировать).
Да.
При вызове RegisterData синхронизация осуществляется автоматически. Старые поля удаляются, новые - добавляются. Здесь есть один момент: если добавляется новое поле - источник данных, то оно получает Enabled = false, и добавляется без колонок. Надо зайти в меню "Выбрать данные для отчета" и выбрать этот источник данных и его колонки.
Желательно избегать таких ситуаций (смена структуры данных после того, как отчет создан). Здесь есть две проблемы:
- если в объектах "Текст" есть ссылка на старое поле (которое было удалено в результате синхронизации), отчет выдаст ошибку при запуске.
- если бэнд "Данные" подключен к источнику данных, который был удален в результате синхронизации, то тоже ничего хорошего не произойдет.
Чтоб отчет не выпадал с ошибкой, я и использую методы CheckEmbeddedDataSources и тот, что его вызывает. При построении отчета, при неудачной проверке поябляется окно "Бла Бла отчет устарел, невозможно построить отчет, обновите шаблон и т.д.". При вызове дизайнера, появляется окно с именами источников, которые устарели, и пользователь должен ручками исправить привязку данных там где это необходимо.
Если в дизайнере вызвать просмотр отчета, то выпадают ошибки, а бэнд сам отвязывается от данных, иногда даже сам перепривязывается к нужному источнику. Вообщем творит чудеса. А вот текстовые поля... Да, пользователь будет их собственноручно фиксить, и пока все не исправит отчета не получит .
Избежать таких ситуаций, к сожалению не получится: бизнес-объекты собираются в рантайме, и в немже могут меняться как угодно.
Оснавная проблема в том, что отчет который храниться в памяти может отличаться от сохраненного в файле. Например, загрузили отчет из файла, отредактировали, но при закрытии дизайнера выбрали "не сохранять". в файле изменения не схранились, а в объекте в памяти - сохранились. Приходится в методе закрытия редактора плясать с бубном:
При вызове Designer.Report.Load(Designer.Report.FileName); возникает такая ситуация, что в отчете снова появляется старый ситочник данных и вменсто одного источника данных в отчете их два: новый и старый.
Вот простой пример запускаем, ставим галочки на источнике данных, сохраняем отчет с именем test.frx в папку Application.StartupPath
раскоменчиваем код и переименовываем регистрируемый объект: В пошаговой отладке в месте (1*) видно что у отчета 2 источника данных:
report.Dictionary.DataSources[0] != null //старый источник
report.Dictionary.DataSources[1] != null
=> Старые поля остаются, новые - добавляются.
Э нет, так не пойдет
Откуда в данном случае FastReport узнает, что надо обновить старый источник данных? Привязка-то идет по имени, которое указано в RegisterData! Естественно, в этом случае будет два источника - старый, не привязанный к данным, и новый.
Подумаю, как лучше сделать.
Он и не должен знать, хотелось бы чтоб новый источник данных регистрировался, а старый, для которого небыли переданны данные, удалялся (или хотя бы можно было определить, что данных для объекта нет и удалить его вручную).
Привязка, как я понимаю, везде идет по именам/алиасам - у бэндов, полей, словаря данных... Просто, например, был в отчете бизнес-объект с вложенным полем "Имя", а потом это поле переименоавль в "Имя42". По хорошему, в этом случае необходимо отчистить словарь шаблона, по новой перерегистрировать данные шаблона отчета и активоровать их. Но если вызвать метод Clear словаря, то отчищается и привязка бэндов во всем отчете - ради 1го изменившегося поля заново заставлять пользователя перепривязывать данные по всему отчету крайне нежелательно. По этому хотелось бы иметь механизм отслеживания - есть ли в источнике данных (DataSourceBase, Column и т.д.) зарегистрированном в словаре данные или нет. Скажем, если данных нет, то удаляем этот источник/поле. Если к источнику есть привязанные в отчете бэнды - отвязываем их. С текстовыми объектами правда тут все-равно ничего не поделать, прейдется пользователю мучаться.
Собственно конечная задача - сделать так, чтоб при изменениях в структуре бизнес-объекта для восстановки работоспособности отчета необходимо было внести минимум изменений в его шаблоне. Если какой-то источник данных (Бизнес-обект, поле бизнес-объекта, влошенный источник бизнес-объекта) исчез (был переименован или вообще удален), его не должно быть среди источников данных. Если появился новый источник данных - он должен автоматически попасть в дерево данных и все его поля и вложенные источники должны быть активны (т.е. дерево структуры нового источника должно быть полностью развернуто).
Такие моменты надо отслеживать самому. Пустой бизнес-объект можно определить так:
Насчет "полностью развернуто" - это чревато. Дерево может быть не деревом, а графом. Сейчас степень начального разворота контролируется параметром maxNestingLevel при регистрации. Ну а пользователь может развернуть источник как угодно в окне "Выбрать данные...".
В текущем билде сделано. Если регистрируется объект, у которого появилось новое поле типа IEnumerable, то его структура раскрывается до указанного maxNestingLevel.
ЗЫ "полностью развернуто" - т.е. с учетом maxNestingLevel.