28 / 28 / 11
Регистрация: 08.08.2011
Сообщений: 1,173
1

Длинные цепочки операций. Организация кода

03.08.2013, 15:48. Показов 967. Ответов 5
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Добрый день!

Есть программа, которая периодически проверяет email, получает письма и с каждым письмом проделывает длинную цепочку операций. Каждый последующий шаг делается только в том случае, если ранее не было ошибок (а ошибки могут случиться на любом этапе). Таким образом выполняется до 30 последовательных действий, в итоге отправляется письмо с результатом. Если где-то была ошибка, то необходимо отправить письмо с описанием ошибки.

Вопрос: как вы обычно организовываете такие длинные цепочки проверок и операций? Пишете кучу вложенных try{}catch(){}?
Вопрос 2: как вы обычно протоколируете выполнение таких длинных цепочек?

Добавлено через 2 минуты
Всего где-то 3000 строк кода. Хочется один раз правильно написать и не переписывать потом.

Добавлено через 9 минут
Программа работает в фоновом режиме 24 часа в сутки. Поэтому требуется, чтобы она работала максимально "самостоятельно", вроде службы.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
03.08.2013, 15:48
Ответы с готовыми решениями:

Правильная организация цепочки Tasks в бесконечном цикле (WinForms)
как правильно реализовать подобное? Когда код что-то типа такого - while (true) {...

Организация задержки операций по времени
Есть необходимость отправлять запросы с пятисекундным интервалом. Как организовать этот интервал?...

Организация кода. Проектирование кода
Доброго времени суток! Появилось желание реализовать в учебных целях библиотеку для связанных...

STL: найти все максимальные цепочки подряд идущих положительных чисел с указанием длины каждой цепочки
Создать массив длины N (число N вводится с клавиатуры). Заполнить массив рандомно. Найти все...

5
Неадекват
1494 / 1232 / 246
Регистрация: 02.04.2010
Сообщений: 2,793
03.08.2013, 16:13 2
Я бы сделал модель письма, все необходимые операции закинул в методы расширения с выбросом исключения в случае ошибки. Протоколирование на основе событий. Соответственно основной код примет вид:
C#
1
2
3
4
5
6
7
8
9
10
11
12
try
{
    MailModel.Operation1()
       .Operation2()
       .Operation3()
       ...
       .OperationLast();
}
catch
{
   //Обработка ошибки
}
0
28 / 28 / 11
Регистрация: 08.08.2011
Сообщений: 1,173
03.08.2013, 16:26  [ТС] 3
Письмо - это просто метод доставки данных. Периодически программа заглядывает в почтовый ящик, проверяет новые письма. Письма проверяются на валидность адреса (несколько категорий), наличие вложенных архивов, эти архивы распаковываются (часто бывают ошибки), проверяется состав файлов, потом каждый файл обрабатывается своими обработчиками, результат архивируется, составляется письмо, которое отправляется обратно.

Добавлено через 4 минуты
Соответственно, мы не работаем постоянно с объектом "письмо". Мы сначала работаем с объектом "клиент подключения", потом с объектом "письмо", потом с объектом "архив", потом по отдельности с каждым вложенным файлом, потом опять с архивом (результирующим), потом с объектом "ответное письмо", потом опять с "клиентом подключения".
0
Неадекват
1494 / 1232 / 246
Регистрация: 02.04.2010
Сообщений: 2,793
03.08.2013, 16:35 4
Мы говорим о разных уровнях абстракции. Поднимитесь чуть выше и создайте два объекта: MailManager - отвечающий за прием и отправку писем и EMail - письмо к которуму применяем все операции. Код станет намного проще и читабельнее.
0
1274 / 975 / 113
Регистрация: 12.01.2010
Сообщений: 1,971
03.08.2013, 17:14 5
даже 3 объекта
один для приема/отправки
само "письмо"
и еще объект отвечающих за обработку

в итоге 3 стадии
1 получить
2 обработать
2 отправить (в том числе и ошибку)
1
Master of Orion
Эксперт .NET
6100 / 4956 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
04.08.2013, 02:12 6
m0nax, приведу пример, который у меня сейчас на работе (не очень оптимально сделано, но я все же еще учусь, тем более, что это старая версия, в новой я рефакторил достаточно много )
Если коротко: есть служба, она преодически пинает мой класс, который соответственно получает письма, парсит их и пытается добавить в базу.
код
Программная реализация
Запускается стандартный Win-Service, который периодически проверяет почту и выполняет все требования, описанные в документации. Листинг ниже:
IntegrationService.cs
C#
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
(автосгенерированный код опущен)
using System.ComponentModel;
using System.Diagnostics;
using System.ServiceProcess;
 
namespace Lanit.OOSMonitoring.MonitoringIntegrator.IntegrationService
{
    public partial class IntegrationService : ServiceBase
    {
        private readonly EmailProcessor _emailProcessor;
 
        public IntegrationService()
        {
            InitializeComponent();
            _emailProcessor = new EmailProcessor(AddLog);
        }
 
        protected override void OnStart(string[] args)
        {
            AddLog("Starting...");
            var bw = new BackgroundWorker();                                // Создали воркера
            bw.DoWork += (sender, eventArgs) => _emailProcessor.OnElapsed();  // чтобы он запустио одну итерацию проверки почты
            bw.RunWorkerCompleted += (sender, eventArgs) => _emailProcessor.SendTimer.Start(); //Когда закончит, включает таймер, чтобы он дальше постоянно занимался тем же
            bw.RunWorkerAsync();
            AddLog("Started");
        }
 
        protected override void OnStop()
        {
            _emailProcessor.SendTimer.Stop();
            AddLog("Stopped");
        }
        
        private static void AddLog(string log, EventLogEntryType type = EventLogEntryType.SuccessAudit)
        {
            if (!EventLog.SourceExists("OOSIntegrationService"))
            {
                EventLog.CreateEventSource("OOSIntegrationService", "OOSIntegrationService");
            }
            eventLog.Source = "OOSIntegrationService";
            eventLog.WriteEntry(log, type);
        }
    }
}

EmailProcessor.cs

C#
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Mail;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Timers;
using Lanit.OOSMonitoring.MonitoringIntegrator.Integration;
using Lanit.OOSMonitoring.MonitoringIntegrator.Integration.Importers;
using OpenPop.Mime;
using OpenPop.Pop3;
using OpenPop.Pop3.Exceptions;
 
namespace Lanit.OOSMonitoring.MonitoringIntegrator.IntegrationService
{
    public class EmailProcessor
    {
        private readonly Action<string, EventLogEntryType> _logger;
        private readonly Action<string> _sucessLogger;
        
        private const string SubjectMask = "Письмо: <{0}>, отправлено с адреса: {1}, содержание: {2}";
        private static readonly string Outlining = new string('_', 40);
        private static readonly string StartupPath;
        public readonly Timer SendTimer;
 
        static EmailProcessor()
        {
            Assembly entryAssembly = Assembly.GetEntryAssembly();
            StartupPath = entryAssembly != null
                              ? Path.GetDirectoryName(entryAssembly.Location)
                              : Directory.GetCurrentDirectory();
        }
 
        private static string LogPath
        {
            get
            {
                return Path.Combine(StartupPath, "Monitoring") +
                       DateTime.Now.Date.ToShortDateString() + ".logoos";
            }
        }
 
 
        /// <summary>
        /// Конструктор, в качестве параметра принимает коллбек-делегат для логгирования
        /// </summary>
        /// <param name="logger">делегат, вызываемый для ведения логов</param>
        public EmailProcessor(Action<string, EventLogEntryType> logger)
        {
            _logger = logger;
            _sucessLogger = s => logger(s, EventLogEntryType.SuccessAudit);
            SendTimer = new Timer(Configuration.MinutesToRefresh * 60 * 1000.0);
            SendTimer.Elapsed += (sender, args) => OnElapsed();
            EmailParser.SubjectRegex = Configuration.SubjectRegex;
        }
 
 
        /// <summary>
        /// Конструктор для создания чекера, который не ведет логов
        /// </summary>
        public EmailProcessor()
            : this((s, type) => { })
        {
 
        }
 
        /// <summary>
        /// Функция, которая вызывается по таймеру и вызовом обработки сообщений, а также логгированием
        /// </summary>
        public void OnElapsed()
        {
            try
            {
                _sucessLogger("Sending messages");
                ProcessMailMessages();
            }
 
            catch (PopServerNotFoundException ex)
            {
                const string format =
                    "Exception = {0}, Message = {1}, InnerException = {2}, Source = {3}, Data = {4}, PopHostName = {5}, Port = {6}, UseSSL = {7}";
                _logger(
                    string.Format(format, ex, ex.Message, ex.InnerException, ex.Source, ex.Data,
                                  Configuration.PopHostName,
                                  Configuration.Port, Configuration.UseSSL), EventLogEntryType.Error);
                throw;
            }
 
            catch (Exception ex)
            {
                _logger(GetExceptionLogString(ex), EventLogEntryType.Error);
            }
 
            _sucessLogger(string.Format("All messages are processed. Sleeping for {0} minutes",
                                        Configuration.MinutesToRefresh));
        }
 
        /// <summary>
        /// Логинится на ящик и начинает обработку имеющихся на нем сообщений
        /// </summary>
        public void ProcessMailMessages()
        {
            using (var fileLogger = new StreamWriter(LogPath, true))
            {
                fileLogger.WriteLine("Начинаю обработку, время = {0}", DateTime.Now.ToShortTimeString());
                Pop3Client client = null;
                int i = 0;
 
 
                fileLogger.WriteLine("Пытаюсь залогиниться на pop3");
                try
                {
                    client = new Pop3InitializableClient();
                    fileLogger.WriteLine("Начинаю обработку писем");
                    foreach (var message in client.GetMessages())
                    {
                        i++;
                        const string mask = "Обрабатываю письмо №{0}, заголовок {1}, отправлено от <{2}>, дата: {3}";
                        fileLogger.WriteLine(mask, i, message.Headers.Subject, message.Headers.From.Address,
                                     message.Headers.DateSent);
                        //TODO: добавить проверку отправителя (письмо должно приходить только с конкретного ящика)
 
                            EventRecord record;
                            if (!message.TryConvertToEventRecord(out record))
                            {
                                fileLogger.WriteLine("Тема письма не удовлетворяет регулярному выражению");
                                OnException(message, "Тема письма не удовлетворяет регулярному выражению");
                            }
                            else if (ImporterFactory.GetImporter(record.ComponentID).Contains(record))
                            {
                                fileLogger.WriteLine("В базе уже существует запись события с таким ID:\t{0}", record);
                                OnException(message, "В базе уже существует запись события с таким ID");
                            }
                            else
                            {
                                fileLogger.WriteLine("Письмо в порядке, пытаюсь добавить в базу");
                                ImportToDb(record);
                                fileLogger.WriteLine("Письмо добавлено{0}", Environment.NewLine);
                            }
                        }
 
                        client.DeleteMessage(i);
                    }
                }
 
                catch (PopClientException ex)
                {
                    fileLogger.WriteLine(GetExceptionLogString(ex));
                    _logger(GetExceptionLogString(ex), EventLogEntryType.Warning);
                }
 
                finally
                {
                    if (client != null)
                        client.Dispose();
                    if (i == 0)
                        fileLogger.WriteLine("Писем на ящике не обнаружено");
                    else
                        fileLogger.WriteLine("Обработано {0} писем", i);
                }
 
                fileLogger.WriteLine(Outlining);
            }
        }
 
        private static string GetExceptionLogString(Exception ex)
        {
            return string.Format("Some error has occurred :{0}\t{1}\t{2}\t{3}", ex.Message, ex.Data, ex.Source,
                                 ex.StackTrace);
        }
 
        /// <summary>
        /// Удаление тегов
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        private static string RemoveHtmlTags(string html)
        {
            string result = Regex.Replace(html, @"<br\s*/?>", Environment.NewLine, RegexOptions.IgnoreCase);
            return Regex.Replace(result, "<.+?>", string.Empty);
        }
 
        private static void ImportToDb(EventRecord record)
        {
            var parsedEvents = record.Split();
            var importer = ImporterFactory.GetImporter(record.ComponentID);
            foreach (EventRecord e in parsedEvents)
                importer.AddToDB(e);
        }
 
        private static void OnException(Message message, string comment)
        {
            var mail = message.ToMailMessage();
 
 
            string oldsubject = mail.Subject;
            mail.Subject = comment;
 
            using (var smtpClient = Configuration.DefaultSmtpClient)
            {
                string subject = mail.Subject;
                string body = string.Format(SubjectMask, oldsubject, mail.From.Address, RemoveHtmlTags(mail.Body));
 
                smtpClient.Send(Configuration.MailAdress, Configuration.Recepients, subject, body);
            }
        }
    }
}
0
04.08.2013, 02:12
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
04.08.2013, 02:12
Помогаю со студенческими работами здесь

Организация кода
Задача такая: Открывается окно, где требуется ввести путь до БД. Вводишь правильный путь, затем...

Организация кода по категориям
Всем привет :senor:

Правильная организация кода в проекте
Доброго дня! При создании проекта для Windows генерятся два файла - файл формы и файл программы....

Организация задержки выполнения кода конструктора
Добрый день. Такой вопрос возник: Вот у меня есть диалог авторизации, вполне себе работающий. но...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru