понедельник, 22 сентября 2014 г.

Загадочно бесполезный SPList.GetDataTable

Копал SPQuery в dotPeek, и наткнулся на интересный кусок кода:

if (this.m_Query.ConsiderManagedPipe
    && this.m_Query.SafeArrayFlags == null 
    && (this.m_Query.CalendarDate == DateTime.MinValue && !this.m_Query.IncludeMandatoryColumns) 
    && (this.m_Query.ViewFieldsOnly 
        && (this.m_Query.DataTableOptions & SPListGetDataTableOptions.RetrieveLookupIdsOnly) != SPListGetDataTableOptions.None 
        && ((this.m_Query.DataTableOptions & SPListGetDataTableOptions.UseBooleanDataType) != SPListGetDataTableOptions.None 
        && (this.m_Query.DataTableOptions & SPListGetDataTableOptions.UseCalculatedDataType) != SPListGetDataTableOptions.None)) 
    && (!string.IsNullOrEmpty(this.m_Query.ViewFields) 
    && string.IsNullOrEmpty(this.m_Query.ViewAttributes) 
    && (this.m_List.BaseType != SPBaseType.DiscussionBoard && !this.QueryIncludesMultiValueLookup(this.m_Query.ViewFields)) 
    && !this.m_List.HasUniqueScopes))
{
      ULS.SendTraceTag(963012918U, (ULSCatBase) ULSCat.msoulscat_WSS_Database, ULSTraceLevel.Verbose, "SPListItemCollection.EnsureListItemData: Retrieving data through the managed pipe.");
      this.m_bUseManagedPipe = true;
}

Интересно, думаю. Неспроста! Сами посмотрите: условия все такие, логически связанные с performance. Может это какой-нибудь хитрый performance-boost такой?

Ну, начал разбираться...

Оказалось, единственное использование internal-свойста ConsiderManagedPipe - в private методе GetItemsForDataTable, который в свою очередь вызывается уже из публичных методов - GetDataTable и AppendDataTable:

// xref: GetDataTable
// xref: AppendDataTable
private SPListItemCollection GetItemsForDataTable(SPQuery query, SPListGetDataTableOptions flags)
{
  query.ViewFieldsOnly = true;
  query.ConsiderManagedPipe = true;
  query.DataTableOptions = flags;
  return this.GetItems(query);
}

Соответственно, начал думать про этот SPList.GetDataTable. На удивление, я его ни разу в жизни не использовал (использовал только SPListItemCollection.GetDataTable). Но если он есть, значит, думаю, зачем-то ведь он нужен! Тем более что обнаружилась какая-то загадочная ветка выполнения запроса SPQuery, которая срабатывала как раз только в случае вызова SPList.GetDataTable! Поэтому решил потестить на производительность. Нагенерил элементов в список:


И собрал вот такой вот простенький PS-скрипт:

$w = get-spweb http://localhost
$l = $w.Lists["Tasks"]
$q = new-object Microsoft.SharePoint.SPQuery
$q.Query = '<Where><And><Lt><FieldRef Name="PercentComplete" /><Value Type="Number">1</Value></Lt><And><Neq><FieldRef Name="AssignedTo" LookupId="True" /><Value Type="Integer">3</Value></Neq><And><Eq><FieldRef Name="Priority" /><Value Type="Text">(3) Low</Value></Eq><Contains><FieldRef Name="Title" /><Value Type="Text">Prep</Value></Contains></And></And></And></Where>';
$q.ViewFields='<FieldRef Name="Title" /><FieldRef Name="DueDate" /><FieldRef Name="AssignedTo" />';
$pos = $null

(Measure-Command { $ii = $l.GetItems($q); $ii[0]; $ii.Count }).TotalMilliseconds
(Measure-Command { $dt = $l.GetDataTable($q, [Microsoft.SharePoint.SPListGetDataTableOptions]0, [ref] $pos); $dt.Rows[0]; $dt.Rows.Count }).TotalMilliseconds
(Measure-Command { $dt = $l.GetDataTable($q, [Microsoft.SharePoint.SPListGetDataTableOptions]::RetrieveLookupIdsOnly -bor [Microsoft.SharePoint.SPListGetDataTableOptions]::UseBooleanDataType -bor [Microsoft.SharePoint.SPListGetDataTableOptions]::UseCalculatedDataType, [ref] $pos); $dt.Rows[0]; $dt.Rows.Count }).TotalMilliseconds

Конечно, если запускать GetItems и GetDataTable'ы последовательно, после первого запроса врубается кэш. Поэтому пришлось мерять по отдельности, и старательно закрывать окошко PS после каждого одиночного теста.

Результаты в среднем получились примерно такие:


Надпись о том, что во втором случае используются managed pipes, в логи успешно валилась. Правда, скорости это не прибавляло :(

Конечно, очень хотелось, чтобы этот загадочный GetDataTable оказался полезным. Поэтому я упорно продолжал тестить на разных объемах данных, с разными выборками, с разными по сложности запросами. Все одно и то же: хотя цифры скачут на +/-200мс, GetItems почти всегда на 150-300мс быстрее. Даже из-под кэша.

Так что вот такой вот в недрах SharePoint есть загадочно бесполезный код :) Не знаю даже, зачем он нужен :)...

1 комментарий:

  1. Андрей, Здравствуйте!Хочу сразу сказать спасибо за ваши труды))
    Насчет GetDataTable ...https://msdn.microsoft.com/ru-ru/library/bb687949%28v=office.12%29.aspx?f=255&MSPPError=-2147217396
    SPListItemCollection - как я понял не является потокобезопасным, поэтому можно кэшировать объект DataTable

    public void CacheData()
    {
    DataTable oDataTable;
    SPListItemCollection oListItems;

    lock(this)
    {
    oDataTable = (DataTable)Cache["ListItemCacheName"];
    if(oDataTable == null)
    {
    oListItems = DoQueryToReturnItems();
    oDataTable = oListItems.GetDataTable();
    Cache.Add("ListItemCacheName", oDataTable, ..);
    }
    }
    }

    ОтветитьУдалить

Внимание! Реклама и прочий спам будут беспощадно удаляться.

Примечание. Отправлять комментарии могут только участники этого блога.