SystemOutOfMemoryException при использовании OutlookServicesClient

Недавно я начал использовать новый Outlook/Office 365 Rest API и получил большую помощь в отношении аутентификация (новая модель приложения v2.0), области, используя OutlookServicesClient и лучше писать (linq-)queries.

К сожалению, некоторые пользователи сообщили о некоторых проблемах с памятью, которые привели к следующему System.OutOfMemoryException:

    Exception of type 'System.OutOfMemoryException' was thrown. :    at Microsoft.OData.Core.Json.JsonReader.ReadInput()
       at Microsoft.OData.Core.Json.JsonReader.ParseStringPrimitiveValue(Boolean& hasLeadingBackslash)
       at Microsoft.OData.Core.Json.JsonReader.ParseValue()
       at Microsoft.OData.Core.Json.JsonReader.Read()
       at Microsoft.OData.Core.Json.BufferingJsonReader.ReadInternal()
       at Microsoft.OData.Core.Json.BufferingJsonReader.ReadNextAndCheckForInStreamError()
       at Microsoft.OData.Core.Json.BufferingJsonReader.ReadInternal()
       at Microsoft.OData.Core.Json.BufferingJsonReader.Read()
       at Microsoft.OData.Core.JsonLight.ODataJsonLightDeserializer.ParseProperty(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func`2 readPropertyAnnotationValue, String& parsedPropertyName)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightDeserializer.ProcessProperty(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func`2 readPropertyAnnotationValue, Action`2 handleProperty)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightPropertyAndValueDeserializer.ReadComplexValue(IEdmComplexTypeReference complexValueTypeReference, String payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightPropertyAndValueDeserializer.ReadNonEntityValueImplementation(String payloadTypeName, IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, Boolean validateNullValue, Boolean isTopLevelPropertyValue, Boolean insideComplexValue, String propertyName, Nullable`1 isDynamicProperty)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightEntryAndFeedDeserializer.ReadEntryDataProperty(IODataJsonLightReaderEntryState entryState, IEdmProperty edmProperty, String propertyTypeName)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightEntryAndFeedDeserializer.ReadEntryPropertyWithValue(IODataJsonLightReaderEntryState entryState, String propertyName)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightEntryAndFeedDeserializer.<>c__DisplayClass2.<ReadEntryContent>b__0(PropertyParsingResult propertyParsingResult, String propertyName)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightDeserializer.ProcessProperty(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func`2 readPropertyAnnotationValue, Action`2 handleProperty)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightEntryAndFeedDeserializer.ReadEntryContent(IODataJsonLightReaderEntryState entryState)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightReader.ReadEntryStart(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, SelectedPropertiesNode selectedProperties)
       at Microsoft.OData.Core.JsonLight.ODataJsonLightReader.ReadAtEntryEndImplementationSynchronously()
       at Microsoft.OData.Core.JsonLight.ODataJsonLightReader.ReadAtEntryEndImplementation()
       at Microsoft.OData.Core.ODataReaderCore.ReadImplementation()
       at Microsoft.OData.Core.ODataReaderCore.ReadSynchronously()
       at Microsoft.OData.Core.ODataReaderCore.InterceptException[T](Func`1 action)
       at Microsoft.OData.Core.ODataReaderCore.Read()
       at Microsoft.OData.Client.Materialization.ODataReaderWrapper.Read()
       at Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.TryRead()
       at Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.TryStartReadFeedOrEntry()
       at Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.TryReadEntry(MaterializerEntry& entry)
       at Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.<LazyReadEntries>d__0.MoveNext()
       at Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.Read()
       at Microsoft.OData.Client.Materialization.ODataReaderEntityMaterializer.ReadNextFeedOrEntry()
       at Microsoft.OData.Client.Materialization.ODataEntityMaterializer.ReadImplementation()
       at Microsoft.OData.Client.MaterializeAtom.MoveNextInternal()
       at Microsoft.OData.Client.MaterializeAtom.MoveNext()
       at System.Linq.Enumerable.<CastIterator>d__94`1.MoveNext()
       at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
       at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
       at Microsoft.OData.ProxyExtensions.PagedCollection`2..ctor(DataServiceContextWrapper context, QueryOperationResponse`1 qor)
       at Microsoft.OData.ProxyExtensions.DataServiceContextWrapper.<>c__DisplayClass38`2.<ExecuteAsync>b__36(IAsyncResult r)
       at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Microsoft.OData.ProxyExtensions.DataServiceContextWrapper.<ExecuteAsync>d__3a`2.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
       at Shared.Data.Office365.Office365Api.<GetEmailsReceived>d__23.MoveNext()

Мой запрос (чтобы получить количество писем, полученных на дату) выглядит так:

var dtStart = date.Date.ToUniversalTime();
var dtEnd = date.Date.AddDays(1).ToUniversalTime();

var mailResults = await _client.Me.Messages
    .OrderByDescending(m => m.ReceivedDateTime)
    .Where(m => m.ReceivedDateTime.Value >= dtStart && m.ReceivedDateTime.Value <= dtEnd)
    //todo: filter if not in Junk Email and Deleted Folder (maybe with ParentFolderId)
    .Take(500) // todo: add paging to (and maybe make more efficient?)
    .Select(m => new DisplayEmail(m)).ExecuteAsync();

// todo: add paging to (and maybe make more efficient?)
foreach (var message in mailResults.CurrentPage)
{
    emails.Add(message);
}

Я не нашел способа распоряжаться исходниками, да и не считаю это нужным, так как есть только локальные переменные. Результат из списка emails затем сохраняется в базе данных. Использование памяти для моего инструмента было стабильным до того, как я добавил эту функцию, и теперь растет примерно на 10 МБ в час (в зависимости от того, сколько у кого-то электронных писем).

Любые предложения очень ценятся!


person casaout    schedule 02.02.2016    source источник
comment
Если вам просто нужно количество писем, полученных на дату, нужно ли вообще их хранить? Вероятно, это сэкономило бы немало времени и места.   -  person Jon Knight    schedule 03.02.2016
comment
Нет, мне это не нужно. Но что мне нужно изменить? Спасибо!   -  person casaout    schedule 03.02.2016


Ответы (1)


Вы можете попробовать несколько вещей здесь:

  • Уменьшите размер страницы. Вы делаете 500 за раз здесь (.Take(500)), вы можете попробовать уменьшить это количество.
  • Запрашивайте только интересующие вас свойства. Если вы измените свой .Select, чтобы ссылаться на определенные свойства вашей переменной m, API должен использовать параметр запроса $select в запросе API, чтобы ограничить то, что возвращается. Пример см. в этом руководстве.
person Jason Johnston    schedule 04.02.2016
comment
Спасибо Джейсон! Несмотря на то, что я только что выбрал несколько свойств в операторе Select, я могу удалить их все, так как они мне не нужны только для подсчета. Кроме того, теперь я реализовал правильное разбиение по страницам (и сократил Take(500) до Take(20). Я только что выпустил обновление и надеюсь, что оно исправит все проблемы с памятью для пользователей. - person casaout; 04.02.2016