У меня есть проблемы с синхронизацией в 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
Другое дело, что я не поклонник помещать критический код, подобный вашему, в статический класс и статический метод. Вместо этого я бы использовал один экземпляр объекта. Это всего лишь я.