Лишние запросы значений свойств объекта используемого в качестве датасоурса

отредактировано 13:29 Раздел: FastReport .NET
Обнаружил, что запрашиваются свойства, которые не выбраны для отчета (в диалоге Выбрать данные для отчёта ...). Это снижает производительность. Как сделать чтобы запрашивались только те свойства, которые нужны для построения отчёта ?

Комментарии

  • отредактировано 13:29
    Поясните, что значит "запрашиваются"? Как можно воспроизвести проблему?
  • отредактировано 13:29
    Я вижу что свойство запрашивает когда вижу что
    1) срабатывает точка останова, поставленная в код get, после вызова методов report.Load (или report.RegisterData)
    2) по трассировке стека видно, что инициаторами запросов являются методы report.Load (или report.RegisterData)
  • отредактировано 13:29
    Исправлю в одном из следующих билдов. Значения простых св-в типа int запрашиваться не будут.
  • отредактировано 13:29
    Есть еще один источник лишних запросов значений свойств объекта выбранного в качестве датасоурса:

    каждое свойство запрашивается 1 раз при
    report.RegisterData(...);

    и 2 раза при
    report.Show();

    Я не знаю зачем так делается, но считаю, что должно быть достаточно только одного получения значения свойства;
    Это существенно повысит производительность в случае если получение значения свойства связано с тяжелыми операциями, типа запроса в БД (как в моем случае).
    Если многократное получение значения свойства требуется для каких-то механизмов, предлагаю добавить возможность отключения этих механизмов.
  • отредактировано 13:29
    AlexTZ написал: »
    Исправлю в одном из следующих билдов. Значения простых св-в типа int запрашиваться не будут.


    Значения свойств возвращающих ссылочные (непростые) типы тоже запрашиваются даже, если они не включены в данные для отчета. Это происходит 1 раз при report.RegisterData(...);
  • отредактировано 13:29
    В идеале свойство должно запрашиваться 1 раз только если его значение используется в отчёте.
    Если свойство есть в выбранных данных, но не используется в отчете, то его значение не должно запрашиваться.
  • отредактировано 13:29
    Свойства запрашиваются для каждой строки данных в источнике. Если отчет двухпроходный, то это значение удваивается. Ну и плюс 1 раз - при регистрации данных - для проверки, не появились ли новые поля, и не исчезли ли старые.
  • отредактировано 13:29
    Да, еще. При обращении к вложенному свойству происходит обращение ко всем св-вам цепочки:
    [MyObject.ComplexProperty.SimpleProperty] - будет каждый раз браться значение св-ва ComplexProperty, затем - SimpleProperty.
  • отредактировано 13:29
    Можно один раз запросить значение свойства, запомнить это значение и далее использовать запомненное значение.
    По поводу проверки на новые или старые поля: для этого наверняка используется reflection, но тогда непонятно зачем значения свойств.
    Если разработчик точно знает, что набор полей в классе не изменится, то было бы полезно вообще отключить проверку на старые и новые поля.

    Я наверное не всё понимаю, но пожелание остается в силе.
  • отредактировано 13:29
    написал:
    Можно один раз запросить значение свойства, запомнить это значение и далее использовать запомненное значение.
    Значение меняется от строки к строке. Как-то его запоминать в пределах одной строки не представляется возможным.
    написал:
    По поводу проверки на новые или старые поля: для этого наверняка используется reflection, но тогда непонятно зачем значения свойств.
    Это необходимо, т.к. тип свойства зачастую не дает всей нужной информации. Например, если это ArrayList.
    По поводу этой проверки я уже написал, что ситуация улучшится в следующем билде (завтра).
  • отредактировано 13:29
    AlexTZ написал: »
    Значение меняется от строки к строке. Как-то его запоминать в пределах одной строки не представляется возможным.

    Можно построить DataSet на основе объектного датасоурса, а потом обращаться только к этому DataSet'у.
  • отредактировано 13:29
    Нет, нельзя. Это большие затраты времени и памяти, если объект содержит большое количество записей и/или имеет обширную структуру.
  • отредактировано 13:29
    AlexTZ написал: »
    Нет, нельзя. Это большие затраты времени и памяти, если объект содержит большое количество записей и/или имеет обширную структуру.


    Понятно,
    предлагаю создать утилитный метод, который преобразует List<EntityType> в DataSet в соответствии с датасурсом отчёта. И разработчик уже сам сможет решать, что подпихивать List<EntityType> или DataSet.

    Бывают отчёты
    детализированные: в них много данных, но они простые (быстро вычисляются) - здесь лучше List<EntityType>
    а бывают,
    агрегатные - в них наоборот мало данных, но они сложные (агрегируют много простых, долго вычисляются) - здесь лучше DataSet.

    У меня агрегатный отчет. Я сам могу конечно DataSet сделать, но лучше чтобы это report сделал, так как он знает какие свойства нужны для отчёта.
  • отредактировано 13:29
    Извините, но это не входит в наши планы. Кроме того - за счет чего будет быстрее работать DataSet? Ведь чтобы заполнить его данными из объекта, нужно проделать ту же самую работу, что делает FastReport при построении отчета. Т.е. перебор записей и обращение к каждому свойству, которое требуется в отчете.
  • отредактировано 13:29
    AlexTZ написал: »
    Извините, но это не входит в наши планы. Кроме того - за счет чего будет быстрее работать DataSet? Ведь чтобы заполнить его данными из объекта, нужно проделать ту же самую работу, что делает FastReport при построении отчета. Т.е. перебор записей и обращение к каждому свойству, которое требуется в отчете.

    Работать будет быстрей так как при создании DataSet значение свойства объекта будет запрашиваться только один раз. DataSet в этом случае выступает в роли кэша. Это выгодно когда для получения значения свойства требуется запрос к БД или другая дорогая операция. В принципе вместо DataSet можно разработать какую-нибудь более легковесную специализированную структуру данных для кэширования значений свойств.
  • отредактировано 13:29
    Могу предложить следующее решение проблемы:
    Сделайте класс с нужным набором св-в и заполните экземпляр класса нужными данными, самым для Вас оптимальным способом. И его уже регистрируйте в отчете.
  • отредактировано 13:29
    Предположим есть свойство которое возвращает данные из БД.
    При построении отчета был сделан 1-й запрос значения свойства. Далее данные в БД меняются.
    2-й запрос значения свойства возвращает уже другое другое значение.
    Что тогда происходит?

    Тоже самое, если значение свойства меняется в другом потоке или для получения значения свойства требуется пользовательский ввод.
  • отредактировано 13:29
    AlexTZ написал: »
    Могу предложить следующее решение проблемы:
    Сделайте класс с нужным набором св-в и заполните экземпляр класса нужными данными, самым для Вас оптимальным способом. И его уже регистрируйте в отчете.

    В качестве этого экземпляра класса может выступать DataSet. Я могу это сделать, но мне кажется report с этим может лучше справиться, так как знает какие данные нужны для отчёта.
  • отредактировано 13:29
    написал:
    Что тогда происходит?
    FastReport берет самое актуальное значение св-ва. Это в случае, когда объект регистрируется напрямую, без класса-"прокладки".

    Давайте лучше рассуждать, используя конкретный пример (кусок кода). Я может чего-то не понимаю, но, мне кажется, в существующем механизме оптимизировать нечего.
  • отредактировано April 2010
    Что такое класс "прокладка"? Может это то что мне нужно?
    Как определяется актуальность значения свойства?

    Пример кода:
    public class ReportObject
    {
        Random _random = new Random();
    
        // это поле выбрано в качестве данных для отчёта
        public int ReportField
        {
             get
             {
                  Thread.Sleep(2000);  // дорогая длительная операция
                  return _random.Next();
             }
        }
    
        // это поле HE выбрано в качестве данных для отчёта
        public object ReferenceTypeField
        {
             get
             {
                  Thread.Sleep(2000);  // дорогая длительная операция
                  return null;
             }
        }         
    }
    
    ReportObject reportObject = new ReportObject();
    report.Load(reportFileFullName); 
    report.RegisterData(new []{reportObject}, "ReportObject_DataSource");  // 1 вызов reportObject.ReportField, 1 вызов reportObject.ReferenceTypeField
    report.Show(); // 2 вызова reportObject.ReportField
    

    потеряли 4 секунды при вызовах eportObject.ReportField, чего можно было избежать если закэшировать значение reportObject.ReportField при первом запросе
    еще потеряли 2 секунды на ненужный запрос reportObject.ReferenceTypeField


    я бы хотел так:
    ReportObject reportObject = new ReportObject();
    report.Load(reportFileFullName); 
    DataSet reportObject_DataSet = report.BuildDataSet(new []{reportObject}, "ReportObject_DataSource"); // 1 вызов reportObject.ReportField
    report.RegisterData(reportObject_DataSet, "ReportObject_DataSource");  // 0 вызовов reportObject.ReportField
    report.Show(); // 0 вызовов eportObject.ReportField
    
  • отредактировано 13:29
    Под классом-"прокладкой" я имел в виду отдельный класс с набором свойств, без какой-либо логики:
    public class MyClass
    {
      public int ReportField { get; set; }
      public object ReferenceTypeField { get; set; }
    }
    
    Вы заполняете этот класс данными из своего объекта:
    MyClass myObj = new MyClass();
    ReportObject reportObject = new ReportObject();
    myObj.ReportField = reportObject.ReportField;
    myObj.ReferenceTypeField = reportObject.ReferenceTypeField;
    report.RegisterData(new [] { myObj }, "ReportObject_DataSource");
    
  • отредактировано 13:29
    Вот я и хочу, чтобы FR сам умел создавать такие объекты прокладки. Я предложил, чтобы тип этого объекта был DataSet.
    Я думаю, я не единственный человек, который имеет сущности с "тяжелыми" свойствами и хочет использовать эти сущности в качестве источников данных для отчетов. Особенно это актуально для ORM сущностей с lazy load свойствами.

    Другой вариант, чтобы FR всё-таки кэшировал значения свойств и не запрашивал их значения по несколько раз.
  • отредактировано 13:29
    Нет, такая функциональность не планируется.

    В новом билде, запрашивать свойства по несколько раз FR будет только в том случае, если они несколько раз используются в отчете. Например, св-во ReportField печатается дважды в разных объектах "Текст".
  • отредактировано 13:29
    А ReferenceTypeField будет запрашиваться? (оно не выбрано в качестве данных для отчета)

    И ещё:
    public class ReportObject
    {
    
        // это поле выбрано в качестве данных для отчёта
        public List<ReportChildObject> ReportChildObjectList
        {
             get
             {
                   return DB.GetReportChildObjectList();  // долгая операция
             }
        }
    
        // Свойства          
    }
    
    public class ReportChildObject
    {
        // Свойства
    }
    

    В отчете соответственно есть родительский (для ReportObject) и дочерний банд (ReportObject.ReportChildObjectList) данных;
    Хотелось бы чтобы ReportObject.ReportChildObjectList запрашивалось 1 раз.




  • отредактировано 13:29
    ReferenceTypeField будет запрошено только при первой регистрации данных (когда создается новый отчет). При регистрации данных в существующем отчете (а также при его запуске) - не будет.

    ReportChildObjectList сейчас запрашивается лишний раз при регистрации данных. Этот лишний запрос я уберу с помощью св-ва UsePropValuesToDiscoverBO в следующем билде. Его надо установить в false перед регистрацией данных:

    Config.ReportSettings.UsePropValuesToDiscoverBO = false;
    report.RegisterData(...);

    при запуске отчета лишних запросов не будет (по одному на каждую строку master). Также у бэнда master надо будет установить св-во PrintIfDetailEmpty = true.

Оставить комментарий

Многофункциональный текстовый редактор. Чтобы отредактировать стиль параграфа, нажмите TAB, чтобы перейти к меню абзаца. Там вы можете выбрать стиль. По умолчанию не выбран ни один стиль. Когда вы выберете текст, появится встроенное меню форматирования. Нажмите TAB, чтобы войти в него. Некоторые элементы, такие как многофункциональные вставки ссылок, картинок, индикаторов загрузки и сообщений об ошибок могут быть вставлены в редактор. Вы можете перемещаться по ним, используя стрелки внутри редактора и удалять с помощью клавиш delete или backspace.