Pull to refresh

Outlook как сервер микросервисов

Reading time8 min
Views3K

Доброго времени суток. 

Ты ж у нас один программист !!!
Небольшая вводная. Я для друзей, по их запросам, выгружаю данные из MS SQL Server'а. Друзья дают исходные данные, для которых надо сделать выгрузку в файлах .csv. Исходных данных (ИД) может быть от 1 до ... строк. Я загружал данные в sql таблицу с помощью задачи или Task в английской версии в SQL Server Management Studio (SSMS). Исторически сложилось, что все sql файлы со скриптами хранятся на моем ПК. Я файлы открывал в SSMS и запускал на выполнение. Результаты записывал в файл и отправлял сообщение, что обработка выполнена. Друзья забирали файлы с результатами. 

Но в один творческий день пришла идея автоматизировать этот процесс, чтобы Друзья все делали сами, с минимальным моим участием. 

Была написана программа на C# + VBA для Outlook'а, которые все это делают. Ниже вкратце их опишу. Программа написана как консольное приложение, никакой диалог с пользователем не предполагается. 

Ниже - описание основных моментов программы. 

Действующие лица 
Друзья: 
Иванов Иван Иванович, его email
iii@corporation.ru 
Петров Петр Петрович, его email
ppp@corporation.ru 
Программа: 
SuperPuperProgram spp.exe 
Я: 
Васильев Василий Васильевич
 

1. Так как выборку надо делать для нескольких друзей (теоретически, фактически запускает 1), необходимо определить, для кого запущена программа. Почему друг передается как аргумент в командной строке - будет понятно чуть ниже. 


switch (args[0]) 

case "Иванов": 
   strEmail = iii@corporation.ru; 
    break;     case "Петров": 
    strEmail = ppp@corporation.ru; 
    break; 

 

2. Так как для ввода ИД используется одна таблица, необходимо исключить запуск нескольких экземпляров программы. (Можно для каждой обработки создавать уникальную sql таблицу для ввода ИД, но учитывая, что эта выгрузка нужна несколько раз может в день, может в неделю и занимается этим 1 человек, я решил использовать Mutex (как прежде неизведанное). 

Объявляем переменную isNew и по значению, возвращенному из функции Mutex определяем запущен ли уже экземпляр программы. Если экземпляр уже запущен, освобождаем ресурсы Mutex'а, формируем строку ответа и отправляем email для кого была запущена программа, выходим из программы. 

bool isNew; 
var mutex = new System.Threading.Mutex(true, "SuperPuperProgram", out isNew); 
if (isNew) 

    InstanceCheckMutex = mutex; 

else 

    mutex.Dispose(); 
    strTextOut += "Программа уже запущена !!!<br />Одновременный запуск нескольких экземпляров программы невозможен !!!<br />Запуск отменен<br />"; 
    sendEMailThroughOUTLOOK(ref strTextOut, strEmail); 
    return; 

В папке с программой есть 2 папки, назовем их так: папка исходных данных (ПИД) и папка результатов (ПР). В папке ПИД находятся csv файлы с ИД, в папку ПР будут записаны файлы с результатами. 

Получаем список csv файлов из ПИД и для каждого файла из списка


foreach (string s1 in csv_In) 

    using (System.IO.FileStream fs = System.IO.File.OpenRead(s1)) 
    { 

Определяем кодировку файла. Для определения кодировки файла я использовал Ude.NET. 

        Encoding e1 = DetectFileEncoding(s1); 

Импорт данных нам поможет сделать bcp.exe. Методом научного тыка найден рабочий вариант запуска процесса выполнения bcp.exe. 

        System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(); 
        processStartInfo.FileName = @"C:\Windows\system32\cmd.exe"; 
        if (e1.EncodingName.Contains("UTF")) 
        { 
            processStartInfo.Arguments = "/c bcp.exe " + "[sqlTable] in \"" + s1 + "\" -S sqlServer -T -c -C " + e1.CodePage + " /t"; 
        } 
        else 
        { 
            processStartInfo.Arguments = "/c bcp.exe " + "[sqlTable] in \"" + s1 + "\" -S sqlServer -T -c -C " + e1.WindowsCodePage + " /t"; 
        } 
        processStartInfo.CreateNoWindow = true; 
        processStartInfo.UseShellExecute = false; 
        processStartInfo.RedirectStandardOutput = true; 
        System.Diagnostics.Process process = new System.Diagnostics.Process(); 
        process.StartInfo = processStartInfo; 
        process.Start(); 
        string output = process.StandardOutput.ReadToEnd(); 
        process.WaitForExit(); 

ИД в sqlTable внесены. 

Теперь подключаемся к sqlServer'у и выполняем sql скрипт. 

strSQLfile - файл со sql скриптом. 

const string strSQLfile = "path\\file.sql"; 
using (System.IO.StreamReader fr = System.IO.File.OpenText(strSQLfile)) 
{
 

В переменную queryString, заносим данные из strSQLfile. queryString — это строка, содержащая текст запроса. Все комментарии, начинающиеся с "--" удаляем до конца строки. Если этого не делать, они закомментируют всю остальную часть строки queryString).

Комментарии /*...*/ оставляем, они комментируют часть кода и на остальную часть строки queryString не влияют, на выполнении не сказываются. (Я практически не использую комментарии такого плана, поэтому размер queryString сильно не увеличится.) 

string str1 = ""; 
while ((str1 = fr.ReadLine()) != null) 

str1 = str1.Replace("\t", " "); 
if (str1.Length > 2) 

//не берем закомментированные строки целиком 
if (str1[0] != '-' && str1[1] != '-') 

if (str1.Contains("--"))        //убираем комментарии в строке до конца строки, внутренние комментарии /*...*/ остаются 

str1 = str1.Substring(0, str1.IndexOf("--")); 

queryString += str1 + " "; 


else 

if (str1.Contains("--"))        //убираем комментарии в строке до конца строки, внутренние комментарии /*...*/ остаются 

str1 = str1.Substring(0, str1.IndexOf("--")); 

queryString += str1 + " "; 


Теперь у нас в queryString полноценный текст запроса, который можно выполнить.  
Создаем подключение к sqlServer'у. Подключение у нас Trusted_Connection=Yes, другие типы авторизации сервер отвергает. Вариант подключения к серверу по логину и паролю отпадает. Друзья не прописаны на сервере и вариант с регистрацией их на сервере даже не рассматривается. Если Друзья будут запускать программу от себя, то ничего и не будет. Запуск программы от имени другого пользователя тоже не лучший вариант. Поэтому - единственный вариант - запуск от моего имени на моем ПК.  

Ниже, на мой взгляд ничего интересного: создаем подключение, задаем параметры, устанавливаем Connection Timeout=3000, так как неизвестно время выполнения. Как я говорил, на входе может быть и 10 строк, и 99999999. 

using (var connection = new QC.SqlConnection("Data Source = sqlServer;" + "Initial Catalog=sqlDB;" + "Trusted_Connection=Yes;" + "Connection Timeout=3000;")) 

QC.SqlCommand command = new QC.SqlCommand(queryString, connection); 

Определяем, куда будем писать результат и кодировку файла. 

System.IO.StreamWriter sw = new System.IO.StreamWriter(fileOut.csv, false, Encoding.GetEncoding(1251)); 

Открываем подключение. 

connection.Open(); 
if (connection.State == DT.ConnectionState.Open) 


Если подключение открыто успешно, выполняем queryString. 

QC.SqlDataReader reader = command.ExecuteReader(); 

Пишем в вывод шапку. 

sw.WriteLine("..."); 
while (reader.Read()) 


Пишем в вывод данные. 

sw.WriteLine("{0};...", reader[0],...); 
iRet += 1; 


Все закрываем. 

reader.Close(); 

sw.Close(); 
connection.Close(); 

Берем следующий файл в работу (переходим на foreach (string s1 in csv_In)). 
После того, как все файлы обработаны, отправляем письмо с результатами. Сами файлы с результатами в письмо не вкладываю, так как их размер может превысить лимит на вложения. 

sendEMailThroughOUTLOOK(ref strTextOut, strEmail); 

Программа отлажена, работает, скрипты выполняются, письма отправляются. Осталось сделать так, чтобы программу на моем компе запускали Друзья. Появилась творческая мысль - использовать для этого Outlook. ВАЖНОЕ - у Outlook'а  в “Параметрах макросов” должно быть включено “Включить все макросы”. Схема виделась такой - коллега шлет мне письмо с кодовым словом в теме письма, Outlook анализирует отправителя, тему письма и при совпадении условий запускает программу. Осталось только реализовать задуманное.

Пусть кодовое слово будет SuperPuperProgram. 

В Outlook'е, в ThisOutlookSession вставляем 

Private WithEvents myOlItems  As Outlook.Items 
Private Sub Application_Startup() 
Dim olApp As Outlook.Application 
Dim objNS As Outlook.NameSpace 
Set olApp = Outlook.Application 
Set objNS = olApp.GetNamespace("MAPI") 
Set myOlItems = objNS.GetDefaultFolder(olFolderInbox).Items 
End Sub 
 
Private Sub myOlItems_ItemAdd(ByVal Item As Object) 
On Error GoTo ErrorHandler 
  Dim Msg As Outlook.MailItem 
  Dim Ret_Val 
  If TypeName(Item) = "MailItem" Then 
    Set Msg = Item 

Проверяем тему письма 

    If Msg.Subject = "SuperPuperProgram" Then 

Проверяем получателя 

        If Msg.To = "Васильев Василий Васильевич" Then 

Если отправитель прошел проверку, запускаем программу, а Msg.Sender.Name передаем как параметр. По этому параметру будет определяться email, кому отправлять письма и кто запустил программу. 

            If Msg.Sender.Name = "Иванов Иван Иванович" Then 
                Ret_Val = Shell("spp.exe " + Msg.Sender.Name) 
            End If 
            If Msg.Sender.Name = "Петров Петр Петрович" Then 
                Ret_Val = Shell("spp.exe " + Msg.Sender.Name) 
            End If 
        End If 
    End If 
ProgramExit: 
  Exit Sub 
ErrorHandler: 
  MsgBox Err.Number & " - " & Err.Description 
  Resume ProgramExit 
End Sub 
 
Получается, что Outlook можно использовать как сервер микро сервисов. Запросы — это письма Друзей, результаты работы - в папке результатов (ПР), взаимодействие (информирование) Друзей с помощью email'ов. 

Думаю, что если подойти творчески к идее Outlook'а как сервера микро сервисов, то можно и не такое нагородить!!! 

Как вариант, другая программа у меня парсит данные и запускается по письму (запросу). 
Спасибо за внимание, буду рад, если мой труд был не напрасен и кому-то окажется полезным.

Tags:
Hubs:
Total votes 7: ↑4 and ↓3+3
Comments6

Articles