[ELMA3] Создание собственного канала отправки сообщений
В статье приведено два примера создания собственного канала отправки сообщений:
- Создание сообщений отдельными текстовыми файлами на сервере.
- Запись сообщений в базу данных.
В данном примере все сообщения системы (отправка сообщений, оповещения о просрочке задач, активация задач и так далее) будут отправляться через Ваш собственный канал сообщений. С помощью данной точки расширения можно реализовать канал отправки сообщений в Twitter, ICQ, Jabber при необходимости.
Пример отображения данных
Рис. 1. Создание сообщений отдельными текстовыми файлами на сервере
Рис. 2. Запись сообщений в БД
Методы расширения (интерфейса)
Точка расширения (интерфейс) IMessageChannel имеет следующие методы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /// <summary> /// Уникальный идентификатор канала /// </summary> Guid Uid { get ; } /// <summary> /// Имя канала /// </summary> string Name { get ; } /// <summary> /// Имя для отображения /// </summary> string DisplayName { get ; } /// <summary> /// Использовать по умолчанию /// </summary> bool Default { get ; } /// <summary> /// Отправить сообщение /// </summary> /// <param name="message">Сообщение</param> void Send(IMessage message); |
Пример класса точки расширения
Создание сообщений отдельными текстовыми файлами на сервере.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | [Component] public class MessageChannel : IMessageChannel { private readonly Guid _uid = new Guid( "{B2D745F9-9624-40c4-9C07-ABF44281F066}" ); public Guid Uid { get { return _uid; } } public string Name { get { return "TextFileChannel" ; } } public string DisplayName { get { return "Канал отправки сообщений в текстовые файлы" ; } } public bool Default { get { return true ; } } private static readonly string Filepath = Locator.GetServiceNotNull<IRuntimeApplication>().Configuration.Config.FilePath; private static readonly string Fullpath = Path.GetDirectoryName(Filepath); private static readonly string HeadDir = Path.Combine(Fullpath, "Messages" ); public void Send(IMessage message) { //Проверка на наличие сообщения if (message == null ) throw new ArgumentNullException( "message" ); //Проверка получателя var recipient = message.Recipient as IUser; if (recipient == null ) { return ; } string recipientDir = Path.Combine(headDir, recipient.ToString()); if (!Directory.Exists(headDir)) Directory.CreateDirectory(headDir); if (!Directory.Exists(recipientDir)) Directory.CreateDirectory(recipientDir); string path = Path.Combine(recipientDir, string .Format( "{0}_{1}-{2}-{3}.{4}.txt" , DateTime.Now.ToShortDateString(), DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second, DateTime.Now.Millisecond)); if (!File.Exists(path)) { using (StreamWriter file1 = new StreamWriter(path, true )) { file1.WriteLine( "Тема: {0}\r\n Текст сообщения: {1}" , message.Subject, message.FullMessageText); } } } } |
Запись сообщений в базу данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | [Component] public class MessageChannelDB : IMessageChannel { private readonly Guid _uid = new Guid( "{FA1B0A61-B3F6-4f16-A57F-9D6253710D50}" ); public Guid Uid { get { return _uid; } } public string Name { get { return "DBChannelMessage" ; } } public string DisplayName { get { return "Канал отправки сообщений в БД" ; } } public bool Default { get { return true ; } } public void Send(IMessage message) { //Проверка на наличие сообщения if (message == null ) throw new ArgumentNullException( "message" ); //Проверка получателя var recipient = message.Recipient as IUser; if (recipient == null ) { return ; } var author = message.Author as IUser; if (author == null ) { return ; } Dictionary< string , object > parameters = new Dictionary< string , object >(); parameters.Add( "SUBJECT" , message.Subject); parameters.Add( "RECIPIENT" , recipient.Id); parameters.Add( "TEXT" , message.FullMessageText); parameters.Add( "AUTHOR" , author.Id); FireBirdConnection.SqlQuery( "insert into MESSAGES (ID, SUBJECT, RECIPIENT, TEXT, \"DATE\", AUTHOR) values (gen_id(GEN_MESSAGES_ID, 1), @SUBJECT, @RECIPIENT, @TEXT, current_timestamp, @AUTHOR)" , parameters); } } |
В данном примере формируется запрос в базу данных FireBird, который добавляет новую запись в таблицу MESSAGES. Для реализации подключения к базе данных FireBird и создания запроса в базу данных был создан класс FireBirdConnection.cs.
Код класса FireBirdConnection.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | using System; using System.Collections.Generic; using System.Data; using System.IO; using EleWise.ELMA.Logging; using EleWise.ELMA.Runtime; using EleWise.ELMA.Services; using FirebirdSql.Data.FirebirdClient; namespace MessageChannel.Connection { public static class FireBirdConnection { private static readonly string Filepath = Locator.GetServiceNotNull<IRuntimeApplication>().Configuration.Config.FilePath; //Путь до файла конфигурации private static readonly string HeadDir = Path.GetDirectoryName(Filepath); //Директория файла конфигурации private const string DbName = "BASEMESSAGES.FDB" ; //Имя базы данных //Формируем строку подключения private static readonly string Fbconnection = new FbConnectionStringBuilder { DataSource = "127.0.0.1" , UserID = "sysdba" , Password = "masterkey" , Port = 3056, Dialect = 3, ServerType = 0, Database = Path.Combine(HeadDir, DbName), Charset = "UNICODE_FSS" }.ToString(); private readonly static FbConnection Fb = new FbConnection(Fbconnection); public static void SqlQuery( string query, Dictionary< string , object > parameters = null ) { if (Fb.State == ConnectionState.Closed) //если соединение закрыто - откроем его Fb.Open(); //Создаем запрос using (var fbCommand = new FbCommand(query, Fb)) { var fbt = Fb.BeginTransaction(); if (parameters != null ) { foreach (var items in parameters) { fbCommand.Parameters.AddWithValue(items.Key, items.Value); } } fbCommand.Transaction = fbt; try { fbCommand.ExecuteNonQuery(); //для запросов, не возвращающих набор данных (insert, update, delete) надо вызывать этот метод fbt.Commit(); //если вставка прошла успешно - коммитим транзакцию } catch (Exception exception) { Logger.Log.Error(exception.Message); fbt.Rollback(); } } } } } |
В данном примере использовалась база данных FireBird, которую необходимо создать, добавить в неё новую таблицу MESSAGES с полями: ID, SUBJECT, RECIPIENT, TEXT, DATE, AUTHOR. Поле ID необходимо сделать Primary Key и NotNull, а также создать генератор (в примере генератор имеет имя GEN_MESSAGES_ID).
Скрипт создания таблицы в БД FireBird:
1 2 3 4 5 6 7 8 9 | CREATE TABLE MESSAGES ( ID BIGINT NOT NULL , SUBJECT VARCHAR (255), RECIPIENT INTEGER , TEXT VARCHAR (255), "DATE" TIMESTAMP , AUTHOR INTEGER ); ALTER TABLE MESSAGES ADD PRIMARY KEY (ID); |
В следующем примере показано как создать свой канал сообщений с несколькими получателями. Сообщения будут отправляться в базу данных одной транзакцией.
В данном примере все сообщения системы (отправка сообщений, оповещения о просрочке задач, активация задач и так далее) будут отправляться через Ваш собственный канал сообщений. При необходимости с помощью данной точки расширения можно реализовать канал отправки сообщений в Twitter, ICQ, Jabber.
В данном примере реализована точка расширения IGroupingMessageChannel, в которой есть метод public void Send(IMessage message, IEnumerable<EleWise.ELMA.Security.IUser> recipients), позволяющий отправлять сообщения всем получателям сразу. Это позволяет сократить время на отправку сообщений.
Пример отображения данных
Рис. 1. Запись сообщений в БД
Методы расширения (интерфейса)
Точка расширения (интерфейс) IGroupingMessageChannel имеет следующие методы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /// <summary> /// Уникальный идентификатор канала /// </summary> Guid Uid { get ; } – генерировать Uid для своего канала отправки сообщений нужно самостоятельно. /// <summary> /// Имя канала /// </summary> string Name { get ; } /// <summary> /// Имя для отображения /// </summary> string DisplayName { get ; } /// <summary> /// Использовать по умолчанию /// </summary> bool Default { get ; } /// <summary> /// Отправить сообщение /// </summary> /// <param name="message">Сообщение</param> void Send(IMessage message); /// <summary> /// Отправить сообщение сразу нескольким получателям /// </summary> /// <param name="message">Сообщение</param> /// <param name="recipients">Список пользователей - получателей сообщения</param> void Send(IMessage message, IEnumerable<IUser> recipients); |
Пример класса точки расширения
Запись сообщений в базу данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | [Component] public class GroupingMessageChannel : IGroupingMessageChannel { private readonly Guid _uid = new Guid( "{F2E1B073-DADA-4b13-805C-FE2CE3FA9375}" ); public Guid Uid { get { return _uid; } } public string Name { get { return "DBGropingMessageChannel" ; } } public string DisplayName { get { return "Канал отправки сообщений в БД одной транзакцией" ; } } public bool Default { get { return true ; } } public void Send(IMessage message) { var recipient = message.Recipient as IUser; if (recipient == null ) { return ; } Send(message, new [] {recipient}); } public void Send(IMessage message, IEnumerable<EleWise.ELMA.Security.IUser> recipients) { //Проверка на наличие сообщения if (message == null ) throw new ArgumentNullException( "message" ); var recUsers = recipients as IUser[] ?? recipients.ToArray(); if (recUsers.Length == 0) return ; var author = message.Author as IUser; if (author == null ) { return ; } Dictionary< string , object > parameters = new Dictionary< string , object >(); parameters.Add( "SUBJECT" , message.Subject); parameters.Add( "TEXT" , message.FullMessageText); parameters.Add( "AUTHOR" , author.Id); string query = string .Empty; foreach (IUser recipient in recUsers) { query += string .Format( "insert into MESSAGES (SUBJECT, RECIPIENT, TEXT, DATE, AUTHOR) values (@SUBJECT, {0}, @TEXT, current_timestamp, @AUTHOR) " , recipient.Id); } MSSQLConnection.SqlQuery(query, parameters); } } |
Запись сообщений в базу данных
В данном примере формируется запрос в базу данных MSSQL, который добавляет несколько записей в одной транзакции в таблицу MESSAGES. Для реализации подключения к базе данных MSSQL и создания запроса в базу данных был создан класс MSSQLConnection.cs.
Код класса MSSQLConnection.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using EleWise.ELMA.Logging; namespace MessageChannel.Connection { public static class MssqlConnection { //Формируем строку подключения private static readonly string MssqlconnectionString = new SqlConnectionStringBuilder { DataSource = "(local)" , UserID = "sa" , Password = "p@ssworD" , InitialCatalog = "BASEMESSAGES" }.ToString(); private static readonly SqlConnection SqlConnection = new SqlConnection(MssqlconnectionString); public static void SqlQuery( string query, Dictionary< string , object > parameters = null ) { if (SqlConnection.State == ConnectionState.Closed) //если соединение закрыто - откроем его SqlConnection.Open(); //Создаем запрос using (var sqlCommand = new SqlCommand(query, SqlConnection)) { var transaction = SqlConnection.BeginTransaction(); if (parameters != null ) { foreach (var items in parameters) { sqlCommand.Parameters.AddWithValue(items.Key, items.Value); } } sqlCommand.Transaction = transaction; try { sqlCommand.ExecuteNonQuery(); //для запросов, не возвращающих набор данных (insert, update, delete) надо вызывать этот метод transaction.Commit(); //если вставка прошла успешно - коммитим транзакцию } catch (Exception exception) { Logger.Log.Error(exception.Message); transaction.Rollback(); } } } } } |
В данном примере использовалась база данных MSSQL, которую необходимо создать, скрипт создания таблицы в базе данных BASEMESSAGES:
1 2 3 4 5 6 7 8 9 10 11 12 | CREATE TABLE [BASEMESSAGES].[dbo].[MESSAGES]( [ID] [ bigint ] IDENTITY(1,1) NOT NULL , [SUBJECT] [nvarchar](255) NULL , [RECIPIENT] [ bigint ] NULL , [TEXT] [nvarchar](255) NULL , [AUTHOR] [ bigint ] NULL , [ DATE ] [datetime] NULL , CONSTRAINT [PK_MESSAGES] PRIMARY KEY CLUSTERED ( [ID] ASC ) WITH (PAD_INDEX = OFF , STATISTICS_NORECOMPUTE = OFF , IGNORE_DUP_KEY = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON ) ON [ PRIMARY ] ) ON [ PRIMARY ] |