Получаем 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
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 19

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

                    Only users with full accounts can post comments. Log in, please.