/// <summary>
 /// Слить тред с его частью.
 /// </summary>
 /// <param name="src">Исходный тред.</param>
 /// <param name="part">Часть треда.</param>
 public void MergeTree(ThreadTree src, ThreadTreePartial part)
 {
     var linkHashService = Services.GetServiceOrThrow<ILinkHashService>();
     if (src.Posts == null)
     {
         src.Posts = new List<PostTree>();
     }
     var oldPosts = src.Posts.DeduplicateToDictionary(p => linkHashService.GetLinkHash(p.Link));
     foreach (var np in part.Posts)
     {
         oldPosts[linkHashService.GetLinkHash(np.Link)] = np;
     }
     src.Posts = oldPosts.Values.ToList();
     for (int i = 0; i < src.Posts.Count; i++)
     {
         src.Posts[i].Counter = i + 1;
     }
 }
 /// <summary>
 /// Парсить данные треда.
 /// </summary>
 /// <param name="data">Данные.</param>
 /// <param name="link">Ссылка.</param>
 /// <returns>Результат.</returns>
 public ThreadTree ParseThread(BoardEntity2 data, ThreadLink link)
 {
     var entity = GetEntityTree(data);
     var result = new ThreadTree()
     {
         Link = link,
         ParentLink = new BoardLink()
         {
             Engine = CoreConstants.Engine.Makaba,
             Board = link.Board,
         },
         Extensions = new List<PostTreeCollectionExtension>(),
         Posts = data.Threads.SelectMany(p => p.Posts).OrderBy(p => p.Number.TryParseWithDefault()).Select(p => Parse(p, link, false)).ToList(),                
     };
     result.Extensions.Add(new MakabaCollectionExtension() { Entity = entity });
     for (int i = 0; i < result.Posts.Count; i++)
     {
         result.Posts[i].Counter = i + 1;
     }
     return result;
 }
 /// <summary>
 /// Получить короткую информацию о треде.
 /// </summary>
 /// <param name="src">Исходный тред.</param>
 /// <returns>Короткая информация.</returns>
 public ShortThreadInfo GetShortInfo(ThreadTree src)
 {
     if (src == null || src.Posts == null)
     {
         return null;
     }
     var firstPost = src.Posts.FirstOrDefault();
     return GetShortInfo(firstPost);
 }
 /// <summary>
 /// Сохранить тред.
 /// </summary>
 /// <param name="data">Данные.</param>
 /// <returns>Таск.</returns>
 public async Task SaveThread(ThreadTree data)
 {
     var fileName = string.Format(CacheConsts.CacheFileTemplates[CacheConsts.Thread], Services.GetServiceOrThrow<ILinkHashService>().GetLinkHash(data.Link));
     var file = await GetCacheFile(fileName);
     var folder = await GetCacheFolder();
     await WriteCacheXmlObject(file, folder, data, true);
 }