Использование в отчётах подключения приложения

CanSeeCanSee Кировская область, Киров
отредактировано 20:26 Раздел: FastReport .NET
Здравствуйте.
Есть приложение для разработки отчётов. Задача приложения - соединяться с БД, загружать оттуда хранящиеся в определённой таблице отчёты и передавать их компонентам FastReport для обработки или вывода. После обработки, принимать от FastReport новые или изменённые отчёты и сохранять их в БД. Загрузка/сохранение сделано по инструкциям, работает. Но хотелось бы, чтобы в FastReport можно было использовать подключение к БД, которое устанавливается при запуске приложения, и только его. В инструкциях на эту тему нашёл только топик "Передача строки подключения в отчёт". В крайнем случае, придётся так и делать, но хотелось бы кое-что несколько другое. В связи с этим, возникают следующие вопросы.
1. При любых операциях с отчётами в приложении уже есть открытый экземпляр SqlConnection (System.Data.SqlClient.SqlConnection). Логично было бы использовать этот компонент и в отчёте. Есть ли какая-то возможность передачи SqlConnection в отчёт и использования его там? Если нет - то планируется ли добавить такую возможность в будущем? Если да, то примерно к какому сроку?
2. В приложении любой новый отчёт должен работать как раз с той базой данных, к которой уже подключено приложение. Однако, чтобы получить доступ к БД из дизайнера в новом, только что созданном отчёте, пользователю нужно создать подключение, указать строку подключения и т.д. Можно ли сделать так, чтобы при создании отчёта и открытии его в дизайнере в нём уже имелось созданное подключение к БД, к которой уже подключено приложение? Регистрация набора данных (Dataset) приложения не подходит - нужен не Dataset приложения, а именно подключение к базе данных. Такое, которое создаётся при выборе в окне "Данные" действия "Новый источник данных" и настройке подключения к БД SQL Server. Которое позволяло бы пользователю просматривать, какие объекты есть в БД, писать собственные SQL-запросы и т.д. Есть ли какая-то возможность добавить такое подключение при создании нового отчёта программным путём, из кода? Я попытался разобраться, сделав такой источник данных вручную, а потом получив доступ к нему в коде с помощью Report.GetDataSource(). Но ничего не получилось - GetDataSource в этом случае возвращает null, хотя алиас источника данных указан правильно.
3. Нужно, чтобы отчёты сохранялись в БД/файлах без какой-либо информации о подключении. Есть ли какая-то возможность автоматически очищать сохранённую в отчётах информацию о подключении к БД из кода приложения?
4. Возможно ли запретить пользователям использовать другие подключения, кроме подключения к БД, к которой подключено приложение, в дизайнере и при выводе отчёта? Иначе получается, что в комплекте с распространяемой системой поставляется не приложение для анализа данных этой системы, а универсальный построитель отчётов для работы с любой базой данных.

Комментарии

  • CanSeeCanSee Кировская область, Киров
    отредактировано September 2009
    Ещё вопрос по той же теме.
    В отчёте программным путём, как показано в инструкции, создаю параметр, в который передаю строку подключения из своего приложения. Параметр пишу в свойство "ConnectionStringExpression" объекта "Подключение" в окне "Данные". При выводе отчёта на просмотр всё нормально. А вот в дизайнере параметр игнорируется, и если у объекта подключения стёрта строка подключения - дизайнер падает с ошибкой. При этом все изменения в отчёте пропадают.
    Можно ли как-то сделать так, чтобы параметр мог использоваться как значение строки подключения и в дизайнере тоже?
  • отредактировано 20:26
    Здравствуйте,

    По первому сообщению: это можно реализовать, но не совсем удобно. Я доработаю код в соответствии с этим сценарием работы (это займет немного времени - к началу следующей недели будет готово).

    По второму: в design-time вычисление выражений невозможно - для этого требуется компиляция отчета. Используйте другой способ передачи строки подключения.
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Спасибо. Буду ждать :)

    Насчёт использования другого способа подключения. Выбор вариантов тут небогат :) В топике "Передача строки подключения" их описано всего два, и способ с передачей параметра не подходит. Попробовал второй, с использованием события подключения к БД - тоже пока ничего не получилось. Несмотря на то, что в обработчике события назначается строка подключения, при очистке строки подключения и попытке отредактировать источник данных дизайнер всё так же сваливается с той же самой ошибкой.
    Остаётся только ждать реализации новой схемы работы :)
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Способ всё-таки есть, и я его нашёл :)
    AlexTZ, в связи с этим в текущей версии лично меня всё устраивает, и для меня делать ничего не нужно. Но если сделаете - не откажусь от новой версии :) В любом случае, спасибо за быстрый отклик и помощь :)

    Делается так. Надо написать свой класс подключения. Написание класса оказалось делом очень простым - почти всё передрано из инструкции, благо там приведены примеры для MS SQL Server. Осталось только убрать ненужное.
    Получилось хорошо - пользователю нужно только создать новый источник данных класса "Подключение редактора" без указания каких-либо параметров, и он сразу же получает готовое настроенное подключение к БД. При этом строка подключения нигде не показывается :) И не сохраняется :)
    Ниже привожу код - кому надо, пользуйтесь. Вставляем код класса подключения куда-нибудь в файл кода приложения, а потом после соединения приложения с БД регистрируем свой класс подключения:
    FastReport.Utils.RegisteredObjects.AddConnection(typeof(Подключение_редактора));
    
    Также пришлось определить свой редактор подключения, который ничего не редактирует и не показывает никаких контролов. Делается он просто - добавляем в проект новый UserControl (обычный, а не наследованный от FastReport. Наследование проще добавляется вручную). Потом в дизайнере ставим ему размер по вертикали 1, по горизонтали - 316. Потом меняем его код на приведённый ниже - и всё готово :)

    Остались мелочи. Пользователю всё-таки надо создать источник данных, но я думаю, он с этим справится :) Ну и у пользователя остаётся возможность создавать отчёты, подключающиеся к другим БД, но это тоже не особо важно.

    Код класса подключения:
        /// <summary>
        /// Класс для использования в FastReport подключения приложения
        /// </summary>
        public class Подключение_редактора : DataConnectionBase
        {
    
            #region Константы
    
            /// <summary>
            /// Название типа подключения
            /// </summary>
            private const string sConnTypeName = "Подключение редактора";
            
            #endregion
    
            #region Статические переменные
    
            /// <summary>
            /// Внутренняя переменная, используемая для хранения строки подключения
            /// редактора отчётов
            /// </summary>
            private static string sEditorConnStr_;
    
            #endregion
    
            #region Статические функции
    
            /// <summary>
            /// Функция для инициализации внутренней переменной со строкой
            /// подключения редактора отчётов
            /// </summary>
            /// <param name="sConnStrIn">Строка подключения приложения</param>
            internal static void InitEditorConnStr(string sConnStrIn)
            {
                // Сохраняем строку подключения приложения:
                sEditorConnStr_ = sConnStrIn;
            }
    
            #endregion
    
            #region Свойства
    
            /// <summary>
            /// Свойство для передачи строки подключения отчёту. Объявлено как new, потому
            /// что перекрывает свойство базового класса. Возвращает пустую строку.
            /// Доступно для редактирования, но редактирование ни на что не влияет.
            /// </summary>
            public new string ConnectionString
            {
                get { return string.Empty; }
                set { }
            }
    
            #endregion
    
            #region Переопределяемые функции базового класса
    
            /// <summary>
            /// Функция для получения спецсимволов, используемых при обрамлении
            /// идентификаторов базы данных, содержащих спецсимволы
            /// </summary>
            /// <returns>Возвращает строку из двух символов. Первый используется как
            /// открывающий, второй - как закрывающий при использовании идентификаторов
            /// базы данных, содержащих спецсимволы</returns>
            public override string GetQuotationChars()
            {
                // Для MS SQL Server для обрамления длинных имён таблиц в любом случае
                // могут использоваться квадратные скобки:
                return "[]";
            }
    
            /// <summary>
            /// Функция для получения строки подключения с именем пользователя и паролем
            /// из исходной строки подключения. Функциональности не несёт, не должна
            /// использоваться, определена для совместимости. Всегда возвращает пустую
            /// строку.
            /// </summary>
            /// <param name="userName">Логин пользователя</param>
            /// <param name="password">Пароль пользователя</param>
            /// <returns>Возвращает строку подключения приложения. Передаваемые логин
            /// и пароль на неё никак не влияют.</returns>
            protected override string GetConnectionStringWithLoginInfo(string userName,
                string password)
            {
                // Возвращаем пустую строку:
                return string.Empty;
            }
    
            /// <summary>
            /// Функция для получения соединения к БД
            /// </summary>
            /// <returns>Возвращает текущее соединение с БД, используемое приложением
            /// </returns>
            public override DbConnection GetConnection()
            {
                // Создаём новое подключение к БД на основе строки подключения,
                // используемого приложением, и возвращаем его:
                return new SqlConnection(sEditorConnStr_);
            }
    
            /// <summary>
            /// Функция для создания экземпляра DbDataAdapter, специфичного для данного
            /// типа подключения. Создаваемый DbDataAdapter будет использоваться для
            /// заполнения таблиц данными
            /// </summary>
            /// <param name="selectCommand">Текст запроса SQL, на основе которого будут
            /// выбираться данные из таблицы</param>
            /// <param name="connection">Подключение к БД, которое будет использоваться
            /// для выполнения запроса к БД</param>
            /// <param name="parameters">Параметры запроса, если они определены</param>
            /// <returns></returns>
            public override DbDataAdapter GetAdapter(string selectCommand,
                DbConnection connection, CommandParameterCollection parameters)
            {
                // Создаём новый SqlDataAdapter, использующий передаваемое подключение:
                SqlDataAdapter adapter = new SqlDataAdapter(selectCommand,
                    connection as SqlConnection);
                // Добавляем к команде выборки данных создаваемого адаптера переданные
                // параметры:
                foreach (CommandParameter p in parameters)
                {
                    // Добавляем очередной параметр:
                    SqlParameter parameter = adapter.SelectCommand.Parameters.Add(p.Name,
                        (SqlDbType)p.DataType, p.Size);
                    // Устанавливаем значение параметра:
                    parameter.Value = p.Value;
                }
                // Возвращаем свежесозданный адаптер:
                return adapter;
            }
    
            /// <summary>
            /// Функция для создания редактора подключения к БД
            /// </summary>
            /// <returns>Возвращает редактор подключения, который ничего не
            /// редактирует, так как все данные у подключения и так уже есть</returns>
            public override ConnectionEditorBase GetEditor()
            {
                // Возвращаем экземпляр редактора, который ничего не делает с подключением:
                return new NothingConnectionEditor();
            }
    
            /// <summary>
            /// Функция для определения типа параметров, используемых в запросах
            /// </summary>
            /// <returns>Возвращает тип параметров, используемых подключением MS SQL
            /// Server</returns>
            public override Type GetParameterType()
            {
                // Возвращаем типы данных MS SQL Server:
                return typeof(SqlDbType);
            }
    
            /// <summary>
            /// Функция для вывода идентификатора подключения. Идентификатор
            /// используется в визуальных редакторах.
            /// </summary>
            /// <returns>Возвращает название типа подключения.</returns>
            public override string GetConnectionId()
            {
                // Возвращаем название типа подключения:
                return sConnTypeName;
            }
    
            /// <summary>
            /// Функция для получения списка таблиц и представлений из БД
            /// </summary>
            /// <returns>Возвращает список таблиц и представлений из БД</returns>
            public override string[] GetTableNames()
            {
                // Вся реализация передрана из справки по FastReport (только добавлена
                // сортировка списка перед выдачей):
                List<string> list = new List<string>();
                GetDBObjectNames("BASE TABLE", list);
                GetDBObjectNames("VIEW", list);
                list.Sort();
                return list.ToArray();
            }
    
            /// <summary>
            /// Вспомогательная функция для функции GetTableNames(). Добавляет в переданный
            /// список объекты БД указанного типа
            /// </summary>
            /// <param name="name">Тип получаемых объектов БД</param>
            /// <param name="list">Список, в который будут добавлены объекты</param>
            private void GetDBObjectNames(string name, List<string> list)
            {
                // Вся реализация передрана из справки по FastReport:
                DataTable schema = null;
                using (DbConnection connection = GetConnection())
                {
                    connection.Open();
                    schema = connection.GetSchema("Tables",
                        new string[] { null, null, null, name });
                }
                foreach (DataRow row in schema.Rows)
                {
                    list.Add(row["TABLE_NAME"].ToString());
                }
            }
    
    
            #endregion       
            
        }
    
    Код редактора подключения:
        /// <summary>
        /// Класс для редактирования строки подключения класса подключения, использующего
        /// подключение приложения. Ничего не редактирует, потому что строку подключения
        /// приложения редактировать не нужно
        /// </summary>
        public partial class NothingConnectionEditor :
            FastReport.Data.ConnectionEditors.ConnectionEditorBase
        {
            /// <summary>
            /// Конструктор класса
            /// </summary>
            public NothingConnectionEditor()
            {
                // Стандартная функция инициализации компонентов:
                InitializeComponent();
            }
    
            /// <summary>
            /// Функция для получения строки подключения из редактора
            /// </summary>
            /// <returns>Возвращает строку подключения приложения</returns>
            protected override string GetConnectionString()
            {
                // Возвращаем пустую строку:
                return string.Empty;
            }
    
            /// <summary>
            /// Функция для передачи строки подключения в редактор. Ничего не делает
            /// </summary>
            /// <param name="value">Передаваемая строка подключения</param>
            protected override void SetConnectionString(string value)
            {
                // Ничего не делаем, так как строку подключения редактировать не нужно
            }
        }
    
  • отредактировано 20:26
    А Вы молодец :)
    Я бы не догадался предложить такое решение. Но все равно поработаю над вариантом "application connection".
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Да, вариант нужный во многих случаях :)
    Насчёт своего решения с новым типом забыл ещё написать, что в приложении, после подключения к БД, до регистрации типа подключения в FastReport, надо инициализировать статическую переменную со строкой подключения приложения:
    InitEditorConnStr(MyApplicationConnection.ConnectionString);
    
    Иначе работать не будет.
    Ещё такой момент относительно FastReport. Вот сделал я свой тип подключения, назвал его поначалу привычными латинскими буквами. При выборе типа подключения пользователь, выбирающий мой новый тип подключения, видит что-то вроде "Objects, MyAppConnection". Переделал класс, назвал его русскими буквами, благо .NET это позволяет - стало лучше: "Objects, Подключение_редактора". Может, стоит сделать какой-нибудь статический метод у подключения, наподобие GetConnectionId, который возвращал бы понятное название типа подключения? А по умолчанию возвращалось бы то, что есть :)
  • отредактировано 20:26
    Попробуйте так:
    Res.Set("Objects, MyAppConnection", "Мое подключение");
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Попробовал, но пока не получилось. Вызов Res.Set проходит, но при выборе типа всё равно отображается название по умолчанию.
    Класс у меня называется "Подключение_редактора", после подключения к БД и регистрации своего типа вызываю:
    FastReport.Utils.Res.Set("Objects,Подключение_редактора", "Подключение редактора отчётов");
    Но для типа подключения при его выборе отображается "Objects,Подключение_редактора".
    Может в какой-то другой момент надо вызывать функцию регистрации, или какую-то другую строку передавать?
  • отредактировано 20:26
    Да, небольшая ошибка. Поправлю в следующем билде (сегодня ночью).
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Спасибо :) Буду ждать :)
  • отредактировано 20:26
    У себя в приложении использовал следующее:
    Report.Dictionary.Connections[0].ConnectionString = myConnectionString;
    

    В итоге, если исходить из предположения, что connection один - все работает. Можно обойти и всю коллекцию.
    Такое использование допустимо? Такой способ не описан в руководстве, но является, на мой взгляд, самым простым.
  • отредактировано 20:26
    Конечно, допустимо. Не забывайте только сделать проверку:

    if (Report.Dictionary.Connections.Count > 0) ...
  • отредактировано 20:26
    Добавил св-во Config.DesignerSettings.ApplicationConnection. Пробуйте :)
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Спасибо. Попробую приделать к своему приложению :)
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Установил новую версию. Функция FastReport.Utils.Res.Set теперь работает как надо :)
    Насчёт Config.DesignerSettings.ApplicationConnection пока что не совсем понятно. Пожалуйста, объясните, как его использовать :) Хотя бы самые основы :)
  • отредактировано 20:26
    Присваиваете этому св-ву свой объект подключения SqlConnection. Идете в меню "Данные/Новый источник данных", выбираете нужные таблицы или создаете новый запрос. Сменить тип подключения, или отредактировать его, или создать другое подключение нельзя (первая страница мастера в этом режиме скрыта). Также строка подключения не пишется в файл отчета.
    Особенности: FR использует свой коннектор, соответствующий переданному в ApplicationConnection. Для SqlConnection - это MsSqlDataConnection. Поэтому использовать можно те подключения, для которых есть коннектор в FR (это OleDb, MS SQL из штатной поставки, и адд-ины Firebird, MySQL, Postgres, VistaDB, Oracle, IBM DB2, SQLite).
  • CanSeeCanSee Кировская область, Киров
    отредактировано 20:26
    Значит, насколько я понял, если ApplicationConnection установить равным своему объекту подключения - всё остальное будет заблокировано, а если его не трогать - всё будет работать как раньше?
    Замечательно :) Как раз то что надо :) Спасибо :)
  • отредактировано 20:26
    Да, именно так :)
  • отредактировано 20:26
    Отлично работает! :) Мне это тоже помогло. Спасибо!!! :) :) :)
  • отредактировано 20:26
    Да, работает, спасибо!
  • отредактировано 20:26
    Добрый день.

    Хотел бы снова поднять данную тему.
    Вопрос в следующем: как использовать уже созданное подключение клиента при работа FastReport.
    Т.е. не "использовать аналогичное подключение", а именно созданное ранее.

    Сделал OracleDataConnection. И маленький отчет, который выдает значения моих переменных.
    Пытался сделать так:
    FastReport.Utils.RegisteredObjects.AddConnection(typeof(FastReport.Data.OracleDataConnection));
    Dal.Core.DbContext.InitContext(); // Инициализация внутренних переменных приложения.
    FastReport.Utils.Config.DesignerSettings.ApplicationConnection = (Oracle.DataAccess.Client.OracleConnection)Dal.Core.DbContext.Connection;
    
    Не получил результата. Судя по всему было создано новое подключение со старыми параметрами (во всяком случае своих инициализированных переменных не увидел. Везде был null).


    Пытался добавить метод SetConnection в FastReport.Data.OracleDataConnection и потом
               FastReport.Report r = new Report();
                r.Load(FastReport.Utils.Config.ApplicationFolder + "Reports\\FSTest.frx");
                FastReport.Data.OracleDataConnection cnn = new FastReport.Data.OracleDataConnection();
                cnn.SetConnection((Oracle.DataAccess.Client.OracleConnection)Dal.Core.DbContext.Connection);
                r.Dictionary.Connections.Add(cnn);
    
                r.Prepare();
    

    Получаю:
    Cannot access a disposed object.
    Object name: 'OracleConnection'.

    при вызове r.Prepare();


    Как правильно использовать уже созданное подключение?

    Заранее благодарен.

    FastReport версия 1.3.34.0.
    Oracle.DataAccess.dll версия 2.102.2.20 (последний, скачивал 28.01).
    База Oracle 10.2.
    Пример FastReport.Data.OracleDataConnection скачивал тоже где-то 26.01.

  • отредактировано 20:26
    Здравствуйте,

    Использовать "созданное ранее" подключение нельзя. Можно только "использовать аналогичное". Оно создается перед каждым чтением данных из таблицы, и разрушается после этого.
  • отредактировано 20:26
    Здравствуйте.


    А планируется добавление такой возможности?

    У нас достаточно большое количество старого кода, который требует инициализации переменных на сервере перед запуском запросов.
    Возможно получиться обойтись событиями объекта Connection и инициализацией переменных в Oracle.DataAccess.Client.OracleConnection.StateChange, но хотелось бы иметь запасной вариант...

    Заранее спасибо.
  • отредактировано 20:26
    Здравствуйте,

    Сейчас это невозможно, т.к. подключение уничтожается после чтения данных. Не уверен, что удастся что-то изменить в следующих версиях.
  • отредактировано 20:26
    Спасибо за ответ.

    Надеюсь обойдусь инициализацией переменных сессии в GetAdapter.

    А то начальство сочтет переход на FastReport.Net не перспективным и тогда мне будет не актуально....
  • Фирсов АлексейФирсов Алексей Хабаровск
    отредактировано 20:26
    Добрый день. Перечитал весь пост, понимаю, что это то, что мне нужно, но так и не понял всей процедуры.
    У меня есть 2 типа отчетов:
    1. Отдельный отчет, в котором настроены все поля, и задано Соединение. Мне необходимо, чтобы я в программе(соединение с базой MS SQL) указал параметры соединения, указал строку выбора данных(например, "Select * from AllViewSorted where street_id = 4"), и мне открылся сформированный отчет. Как этого достичь?
    У меня имеется такой код:
    report1.Preview = previewControlMain;
    conn = new SqlConnection(Properties.Settings.Default.ConnSTR); //здесь строка подключения к базе
    SqlCommand comm = new SqlCommand(SQLCommand); //SQLCommand - строка выборки, текст указан выше
    SqlDataAdapter daCustomers = new SqlDataAdapter(comm);
    DataSet dsCustomers = new DataSet();
    conn.Open();
    daCustomers.SelectCommand.Connection = conn;
    daCustomers.Fill(dsCustomers, "AllViewSorted");
    report1.Load("ReportForPrg.frx");
    report1.RegisterData(dsCustomers, "AllViewSorted");
    report1.Show();
    this.ShowDialog();
    

    Я попробовал настроить все поля в отчете и потом удалить соединение, для того, чтобы программно его передавать в отчет. Не получается.

    2. Имеются отчеты, которые будут просматриваться через отдельную программу(сделана на основе исходных кодов WINFORM приложения из дистрибутива). Все отчеты построены с использованием одного мастер шаблона, в котором задается подключение. Вопросы:
    а) при изменении подключения в мастер шаблоне изменится ли подключение в дочерних шаблонах?
    б) Как сделать возможность изменения подключения с сохранением данных.

    Для чего все это: программа разрабатывается для одной организации, и данных о подключении там я не знаю. Поэтому и необходимо сделать возможность динамической смены данных о регистрации.
  • отредактировано 20:26
    Здравствуйте,

    1. Проще всего так (подразумевается, что в отчете есть подключение, с одной таблицей):

    report1.Load("ReportForPrg.frx");
    report1.Dictionary.Connections[0].ConnectionString = строка_подключения;
    report1.Dictionary.Connections[0].Tables[0].SelectCommand = текст_sql;
    report1.Show();

    2. У наследованных отчетов масса ограничений, в т.ч. на работу с источниками данных. Использовать наследование в данном случае не советую.

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

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