Получаем SPListItem из SPList. Очень быстро и очень медленно

При профилировании веб-части для SharePoint с удивлением обнаружил узкое место в SPListItemCollection.this[Guid]… Получение элемента списка по Guid'у, фактически первичному ключу, занимало уйму времени на большой коллекции.
Происходило это так

var uniqId = new Guid(/* get GUID somehow */);
SPList list = /* get list somehow */

SPListItem anItem = list.Items[uniqId];


* This source code was highlighted with Source Code Highlighter.
Как есть еще методы получения элемента из списка?
Есть SPList.GetItemByUniqueId(Guid uniqueId) — при использовании не возникает такой задержки. Почему?

Воспользуемся рефлектором, и пореверсинженерим шарепонит…

Вот что мы видим в SPListItemCollection.this[Guid]:

public SPListItem this[Guid uniqueId]
{
  get
  {
    this.EnsureListItemsData();
    this.EnsureFieldMap();
    int iIndex = 0;
    int columnNumber = this.m_mapFields.GetColumnNumber("UniqueId");
    string str2 = uniqueId.ToString("B").ToLower();
    while (true)
    {
      if (iIndex >= this.m_iRowCount)
      {
        throw new ArgumentException();
      }
      string str = ((string) this.m_arrItemsData[columnNumber, iIndex]).ToLower();
      int num3 = SPUtility.StsBinaryCompareIndexOf(str, ";#");
      if ((num3 > 0) && (str.Substring(num3 + 2) == str2))
      {
        this.EnsureListItemIsValid(iIndex);
        if (this.m_iColection == null)
        {
          return new SPListItem(this, iIndex);
        }
        return this.m_iColection.ItemFactory(iIndex);
      }
      iIndex++;
    }
  }
}


* This source code was highlighted with Source Code Highlighter.

Содержимое всей коллекции перебирается элемент за элементом, для каждого элемента вычисляется совпадение GUID'а с заданным. Чем больше коллекция — тем дольше работаем.

А теперь GetItemByUniqueId:
public SPListItem GetItemByUniqueId(Guid uniqueId)
{
  SPQuery query = new SPQuery();
  query.Query = "<Where><Eq><FieldRef Name=\"UniqueId\"></FieldRef><Value Type=\"Guid\">" + uniqueId.ToString("B") + "</Value></Eq></Where>";
  query.ViewAttributes = "Scope=\"RecursiveAll\" ModerationType=\"Moderator\"";
  query.MeetingInstanceId = -2;
  query.QueryOpt = SPQuery.SPQueryOpt.None | SPQuery.SPQueryOpt.UniqueId;
  SPListItemCollection items = this.GetItems(query);
  if (items.Count != 0)
  {
    return items[0];
  }
  while (!(this.ID == this.Lists.Web.UserInfoListId))
  {
    throw new ArgumentException();
  }
  throw new ArgumentException(SPResource.GetString("CannotFindUser", new object[0]));
}


* This source code was highlighted with Source Code Highlighter.

Здесь, используя SPQuery, получаем непосредственно саму запись без перебора коллекции.

Вывод: на больших списках пользоваться SPList.Items[Guid] — долго, непроизводительно. Лучше предпочесть SPList.getItemByUniqueId(Guid);

_________
Текст подготовлен в ХабраРедакторе

UPD: Спасибо vladem — полезная линка на тематический KB — http://msdn.microsoft.com/en-us/library/bb687949.aspx
+22
8 октября 2009, 21:28
9
Olegas 38,4

комментарии (18)

+4
Olegas #
За что минуса? Все всю жизнь знали это?
+7
XaocCPS #
многие люди на хабре ненавидят и боятся .net и sharepoint в том числе, не обращайте внимания на минусы
0
AquilA #
Так ведь это не должно отражаться на авторе, если у кого-то проблемы с .Net и WSS. Статья для тех, кто работает с WSS. Если не нравится игнорьте, а оценивать должны те, кто разбирается в этом.
0
XaocCPS #
я прекрасно это понимаю, но люди не идеальны, поступают по-другому
+3
pieceofsummer #
Минус поставить проще, чем в коде разобраться, ясное дело ;)
–13
LightServer #
Bash форева… :)
0
temaweb #
Bash и .net немного разные вещи, мягко говоря.
0
LightServer #
Для непонятливых и минусующих- это была ирония…
0
Pavel7 #
Крайне бестолковая ;)
0
LightServer #
Уж какая есть… :)
0
Launcher #
Olegas, подскажите, ситуация следующая: имеется список с несколькими сотнями тысяч элементов (вот такое досталось от предыдущих разработчиков, приходится мириться) когда я делаю Items.Count всё подвисает на несколько минут а потом вылетает с ошибкой нехватка памяти, тоже самое если я пытаюсь в цикле (foreach или for не важно) перебрать все элементы, как только обращаюсь к первому элементу всё падает. Приходится caml запросами по чуть чуть доставать элементы. Есть ли какой нибудь более красивый и производительный метод?
+3
Antipod #
Извините не совсем ответ в тему, попробуйте пользоваться ItemsCount свойством, вместо Items.Count
0
Antipod #
Конечно я имел ввиду ItemCount ('s' закралась автоматически при наборе), сорри
0
Launcher #
Спасибо! работает! (хотел тыкнуть зелёную галочку, а оказывается у меня чегото маловато..)
0
outcoldman #
а если по списку foreach'ем то тоже будут проблемы?
0
Launcher #
Если вы о моей проблеме то да
0
Olegas #
foreach'ем по списку — по той же коллекции бегать.
Все мрет именно на создании коллекции ИМХО.
+2
vladem #
Кстати, в MSDN на эту тему есть весьма полезная статья Best Practices: Common Coding Issues When Using the SharePoint Object Model

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.