WoodseyAU Ответов: 1

У меня есть проблемы с синхронизацией в C# и EWS


В следующем фрагменте кода я пытаюсь сохранить (с помощью переименования) вложение к электронной почте на диск.

В большинстве случаев это работает правильно, но иногда это терпит неудачу в fAttachment.Инструкция Load (filename) и пустой файл сохраняются на диск.
Когда это происходит, я получаю это исключение:-

System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 10.0.0.1:443
   at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
   at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.GetResponse()
   at Microsoft.Exchange.WebServices.Data.EwsHttpWebRequest.Microsoft.Exchange.WebServices.Data.IEwsHttpWebRequest.GetResponse()
   at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.GetEwsHttpWebResponse(IEwsHttpWebRequest request) By System On BMXS04


Чтобы добраться до той позиции, где лежит жир.Нагрузка используется требует, чтобы
а) я уже установил действительное соединение ExchangeService
б) я успешно получил список писем с вложениями
в) конкретное электронное письмо, над которым я работаю, было успешно загружено
г) я могу проверить свойства вложения, чтобы убедиться, что оно относится к тому типу, который я хочу сохранить

Итак,когда дело доходит до выдачи Фаттаха.Загрузить все должно быть хорошо.

Сервер exchange находится во внутреннем ящике SBS2011 с пакетом обновления 3 Exchange 2010. Все полностью залатано.
Машина (живая) или разработка - это оба компьютера домена.

Вложения никогда не превышают 4 КБ и являются обычным текстом.

Любой совет будет с благодарностью принят.

(П. С. Первый пост здесь, так что я надеюсь, что я сделал это правильно)

Я санировал следующий код следующим образом:
public static void HandleWeborder(string mhSavePath, string mhEmail)
{

    //
    try
    {
        var service = GetExchangeService();
        //
        if (service != null)
        {
            //
            var StartTime = DateTime.Now;
            bool wasOrder;
            // Allow a maximum of 150 emails per run.
            var view = new ItemView(150);
            var lookUp = mhEmail;
            var searchFilter = new SearchFilter.IsEqualTo(EmailMessageSchema.HasAttachments, true);
            var results = service.FindItems(new FolderId(WellKnownFolderName.Inbox, new Mailbox(lookUp)), searchFilter, view);
            if (results.TotalCount > 0)
            {
                foreach (EmailMessage email in results)
                {
                     wasOrder = false;
                     email.Load(new PropertySet(ItemSchema.Attachments));
                     foreach (Microsoft.Exchange.WebServices.Data.Attachment attachment in email.Attachments)
                     {
                         FileAttachment fAttachment = attachment as FileAttachment;
                         var test = fAttachment.Name.Substring(attachment.Name.Length - 3);
                         if (test == "xtx")
                         {
                            wasOrder = true;
                            var newName = fAttachment.Name.Substring(0, fAttachment.Name.IndexOf(".", StringComparison.Ordinal));
                            if (newName.Substring(0, 5) == "Order")
                            {
                                newName = newName.Substring(6);
                                newName = mhSavePath + "W" + newName;
                                //
                                var suf = ".txt";
                                if (File.Exists(newName + suf)) suf = ".dup";
                                if (File.Exists(newName + suf + ".bak")) suf = ".dup";
                                 newName += suf;
                                 bool dountiltrue = false;
                                 int attempts = 0;
                                 while (!dountiltrue)
                                 {
                                    try
                                    {
                                        fAttachment.Load(newName);
                                        wasOrder = true;
                                        dountiltrue = true;
                                    }
                                    catch (Exception ex)
                                    {
                                       if (attempts < 5)
                                       {
                                           attempts += 1;
                                           var innerEx = ex.InnerException.ToString();
                                           RunParms[PosLogMsg] = "Attempt " + attempts + " - Error " + newName + " May Have timed out - " + innerEx;
                                           RunParms[PosLogType] = LogError;
                                           AddActivity();
                                        }
                                     }
                                  }
                               }
                            }
                         }
                     }
                     if (wasOrder)
                     {
                        email.Delete(DeleteMode.HardDelete);
                     }
                }
            }
        }
    }
    catch (Exception ex)
    {
        //
        //TODO need to log error somehow
        //
        // might disable this logging because it may be pointless
        var innerEx = ex.InnerException.ToString();
        RunParms[PosLogMsg] = "Error 264-9 - " + innerEx;
        RunParms[PosLogType] = LogError;
        AddActivity();
    }
}


Что я уже пробовал:

Я добавил Все эти Try/Catch и вручную шагнул через них, но он не ошибается при шаге, только когда находится в режиме полного запуска :-(

Примечание: отредактировано, чтобы удалить все записи ошибок, так как обработка Try/Catch сбивала с толку некоторых комментаторов. Try/Catch были созданы просто для того, чтобы получить физическую запись возникновения исключения, потому что потребовался бы целый день, чтобы пройти через код и дождаться прерывистого сбоя (сбой происходит только в 3-5% случаев вызова кода и всегда происходит в fAttachment.Нагрузки(новое_имя) заявление

Graeme_Grant

"иногда я ловлю исключение" - что такое исключение? Вы имеете в виду ошибку или время от времени?

Ммм, я вижу пару команд загрузки, но никаких команд сохранения. Я что-то упустил?

WoodseyAU

Спасибо за ваш ответ. Чтобы иметь возможность помочь мне, было бы полезно, если бы Вы были знакомы с API EWS 2.2. the fAttachment.Функция LOAD (string) принимает вложение электронной почты и сохраняет его в местоположение и имя, указанные в строке.

David_Wimbley

Так что просто некоторые комментарии, как ваш код трудно следовать.

* В вашем коде есть безбожное количество операторов try catch. Вы, вероятно, едите где-то исключение и не осознаете этого. Если вам нужны все эти уловы try, прекрасно, но для целей отладки я бы удалил их все, кроме первого, самого высокого уровня, чтобы вы могли ясно видеть, где находятся ваши проблемы.

* У вас есть утверждения if, которые выглядят следующим образом if (test != "xtx") continue; во всем вашем коде. Я не уверен, какова их цель, и мое личное предпочтение состоит в том, чтобы всегда включать скобки для операторов if независимо от того, сколько строк в них находится, поскольку такие вещи легко пропустить. Насколько вам известно, поскольку вы полагаетесь на try/catch для отладки, вы можете столкнуться с одним из этих условий и не осознавать этого.

* Вместо того, чтобы полагаться на try/catch для выполнения отладки, установите точки останова и пройдите через свой код, чтобы точно определить проблему. Try/catch отлично подходит для посмертного анализа вашей проблемы, но точки останова позволяют вам смотреть на ваше приложение во время его работы, вы можете заметить что-то на вкладке locals или что-то еще, что вы не можете увидеть из регистрируемого исключения try/catch.

* Если бы у вас был тайм-аут или что-то еще, ваше исключение сказало бы об этом. Вам нужно предоставить вывод вашего исключения, прямо сейчас это такая же игра в угадайку для нас,как и для вас. Ваша ошибка не ясна

* Похоже, что вам не хватает кода. Как сказал Graeme_Grant, я не вижу нигде в вашем фрагменте кода, где вы пытаетесь сохранить что-либо в файл. Я ожидал бы увидеть что-то вроде email.Save(filepath) или что-то в этом роде.

WoodseyAU

Спасибо за ваши комментарии. Да, здесь полно операторов try/catch, которые использовались для отслеживания приложения в режиме запуска, а не в режиме отладки. Поскольку проблема носит прерывистый характер (то есть в 95% случаев этот процесс выполняется без каких-либо проблем) Я не могу сидеть в режиме отладки и шагать через него. Это займет весь день :-(
Конструкция if (test !- '"xtx") continue была результатом использования resharper. Это (так они говорят) останавливает код, становящийся слишком отступом. Я также иногда сомневаюсь в этом,и это действительно затрудняет ясность.
Вы правы, я не делал исключения. Теперь я отредактирую вопрос.


Graeme_Grant

Чтобы облегчить чтение кода, я бы рефакторировал повторяющийся код в один метод.
Это появляется несколько раз:

RunParms[PosLogType] = LogMail;RunParms[PosLogMsg] = "Checking For New eMails";AddActivity();

К:
private void LogActivity(LogTypeEnum type, string msg)
{
    RunParms[PosLogType] = type;
    RunParms[PosLogMsg] = msg;
    AddActivity();
}

позволяющий:
LogActivity(LogMail, "Checking For New eMails");

А это:
var innerEx = ex.InnerException.ToString();
RunParms[PosLogMsg] = "Error 264-3 - " + innerEx;
RunParms[PosLogType] = LogError;
AddActivity();

До настоящего времени:
LogActivity(LogError, $"Error 264-3 - {ex.InnerException.ToString()}");

Ваш код станет бесконечно легче читать.

WoodseyAU

Спасибо за это. Да, так будет лучше. Я буду реализовывать это и (когда у меня будет время) заменять его на протяжении 11 различных проектов в решении.
Это не решит (к сожалению) мою реальную проблему :-)

Graeme_Grant

Нет, это не решит проблему, но уменьшит объем кода, облегчая отладку. Рефакторинг-это быстрый шаг:
1. Выделите блок кода
2. щелкните правой кнопкой мыши и выберите "Быстрые действия и рефакторинг "(или CTRL-.)
3. дайте новому методу имя и настройте параметры.
4. теперь замените код новым методом.

WoodseyAU

Да, это было чертовски легко :-) Спасибо. Я всегда был напуган (как относительный c# noob) в процессе рефакторинга.

Graeme_Grant

Я бы рефакторинг следующие:

newName = newName.Substring(6);
newName = mhSavePath + "W" + newName;
//
var suf = ".txt";
if (File.Exists(newName + suf)) suf = ".dup";
if (File.Exists(newName + suf + ".bak")) suf = ".dup";
newName += suf;

К:
private string GenerateUniqueFullFilename(string filenamePart, string path)
{
    var newName = System.IO.Path.Combine(path, filenamePart);
    var suf = ".txt";

    if (file.Exists(newname + ".bak") File.Delete(newname + ".bak");
    if (File.Exists(newName + suf)) suf = ".dup";
    if (File.Exists(newName + suf)) suf = ".bak";

    return newName + suf;
}

Затем, чтобы использовать:
newName = GenerateUniqueFullFilename("W" + newName.Substring(6), mhSavePath);

WoodseyAU

Ницца.
Я уже могу представить себе, вероятно, 60% кода в 11 проектах моего решения, где он может быть рефакторизован. Это еще одна причина, по которой я так люблю CP. Я полностью самоучка в C#, VS2017, SQL, WinForms и создании DLL, поэтому отзывы, которые я получаю отсюда, блестящи и всегда с благодарностью принимаются.

Graeme_Grant

Все, что я делаю здесь, - это определение отдельных блоков кода ответственности и их рефакторинг. Это облегчает чтение, упрощает отладку, создает повторно используемый код и уменьшает количество ошибок в повторяющемся коде.

Фу-у-у, я тоже самоучка... С VB на C#, SQL, в Winform для Silverlight в WPF с Xamarin (в MVVM, МЭФ, твердое тело, etc...), SQL, в Asp.Net для MVC, Azure и т. д... ;)

WoodseyAU

У меня действительно есть уровень факторинга (метод AddActivity находится в библиотечном классе и обрабатывает ведение журнала для всех 11 проектов). Я учусь каждый день :-)

Graeme_Grant

Кроме того, я бы провел дальнейший рефакторинг. Переместите тело цикла While в его собственный метод, а затем тело цикла foreach в его собственный метод. Mke его легче читать, разделяет проблемы и легче отлаживать.

Graeme_Grant

Вопрос: Являются ли какие-либо из методов, вызываемых вне приведенного выше кода, асинхронными?

WoodseyAU

Весь этот метод вызывается backgroundworker. Backgroundworker запускается только в том случае, если он еще не занят. Все мои методы чисто синхронны. За методы в EWS API 2.2 я не могу поручиться.

Graeme_Grant

Может быть, поэтому. Многопоточность печально известна этим и может быть трудно уловима.

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

private static object lockObj = new object();

public static void HandleWeborder(string mhSavePath, string mhEmail)
{
    lock(lockObj)
    {
        // code goes here
    }
}

WoodseyAU

Нужна ли мне разблокировка?

Graeme_Grant

Нет,все это замкнуто... Подробнее об этом вы можете прочитать здесь: оператор Lock (Справочник по c#) | Майкрософт документы[^]

Graeme_Grant

Другое дело, что я не поклонник помещать критический код, подобный вашему, в статический класс и статический метод. Вместо этого я бы использовал один экземпляр объекта. Это всего лишь я.

1 Ответов

Рейтинг:
12

Graeme_Grant

Просто еще одна мысль, не зная, находятся ли приложение и сервер хранения в удаленных местах друг от друга или находятся на одном сервере, это может быть просто проблема задержки сети, вызывающая временную проблему.

Если они удалены друг от друга, то вам может понадобиться лучший механизм повторной попытки. Вот такой RetryOperationHelper[^] это может быть адаптировано к вашим требованиям. Я бы также рекомендовал реализовать Экспоненциальный откат[^] стратегия, позволяющая обеспечить большую долговечность.


WoodseyAU

Сервер Exchange Server/ Storage server (контроллер домена SBS2011 и т. д.) и App PC (сервер 2012 с запущенным SQL Server и другими приложениями) физически находятся примерно в 3 дюймах друг от друга. По lan-кабелю примерно 2 метра через коммутатор 1 ГБ. Отставание должно быть очень близко к нулю :-)
Я посмотрю ваши рекомендации, когда представится возможность. Как одинокий ИТ-специалист в небольшой компании, я, как правило, ежедневно борюсь с множеством различных проблем, особенно после того, как обновления M$ грубо навязываются нам).

Я реализовал блокировку, и она совершенно не изменила производительность. Код работает отлично до сих пор и ждет, когда произойдет исключение (как это происходит 2-3 раза в день).

Graeme_Grant

Да, этого не должно произойти, но проблема указывает на то, что локальное приложение отказалось от удаленного сервера. Возможно, проблема заключается в том, что удаленный сервер слишком долго обрабатывает заданную задачу, вызывая тайм-аут локального сервера. Возможно, либо улучшить время, необходимое удаленному серверу, либо продлить время ожидания на локальном сервере для размещения.

Другой вопрос: выполняет ли удаленный сервер какие-либо периодические задачи/задания, которые могут вызвать какую-либо задержку? Есть ли у удаленного сервера запланированная перезагрузка или ежедневный план резервного копирования? Это также может вызвать возникшие проблемы.

WoodseyAU

Произошло исключение.
Поскольку я поместил его в этот цикл повторных попыток (который я реализовал только сегодня), он попробовал еще раз и успешно справился с ним. Как вы и предполагаете, это указывает на проблему в самой службе обмена. У него уже есть 60-секундный тайм-аут, установленный в его initliasation. Я продлю его до 120 и посмотрю, получу ли я еще исключение.
Что касается причин тайм-аута, то этот конкретный блок работает на cpu 4-7%, mem-на 90% (из-за того, что хранилище exchange выделяется кучами). Я думаю, что исключением, хотя и указывающим на то, что виновником является exchange, на самом деле может быть задержка очереди diskio, поскольку сервер также является основным общим ресурсом карты дисков для домена. Если это так, то продление тайм-аута обмена не должно решить проблему.

Graeme_Grant

Будем надеяться, что продление тайм-аута решит эту проблему для вас.