/// <summary>
        /// Получить все <see cref="TeamSlice"/> для конкретного проекта с момента последней проверки.
        /// </summary>
        /// <param name="project">
        /// Проект для которого необходимо получить все <see cref="TeamSlice"/>.
        /// </param>
        /// <returns>
        /// Все <see cref="TeamSlice"/> для указанного проекта с момента последней проверки.
        /// </returns>
        private List <TeamSlice> GetTeamSlices(TFSProject project)
        {
            ExternalLangDef            languageDef = ExternalLangDef.LanguageDef;
            LoadingCustomizationStruct lcs         = LoadingCustomizationStruct.GetSimpleStruct(typeof(TeamSlice), TeamSlice.Views.TeamSliceE);
            VariableDef startDate = new VariableDef(languageDef.DateTimeType, Information.ExtractPropertyName <TeamSlice>(n => n.StartDate));
            VariableDef endDate   = new VariableDef(languageDef.DateTimeType, Information.ExtractPropertyName <TeamSlice>(n => n.EndDate));

            // Условие: либо посленяя дата проверки или текущая дата попадают в дипазон дат среза команды, либо диапазон дат среза команды содержится между ними.
            Function dateLimit = languageDef.GetFunction(
                languageDef.funcOR,
                languageDef.GetFunction(languageDef.funcBETWEEN, project.LastCheckDate, startDate, endDate),
                languageDef.GetFunction(languageDef.funcBETWEEN, _now, startDate, endDate),
                languageDef.GetFunction(
                    languageDef.funcAND,
                    languageDef.GetFunction(languageDef.funcL, endDate, _now),
                    languageDef.GetFunction(languageDef.funcG, startDate, project.LastCheckDate)));

            string   agregatorName = Information.ExtractPropertyName <TFSProjectForReview>(n => n.Team);
            string   detailPath    = Information.ExtractPropertyPath <TeamSlice>(n => n.Team);
            Function projectLimit  = languageDef.GetFunction(
                languageDef.funcExist,
                new DetailVariableDef(languageDef.DetailsType, agregatorName, TFSProjectForReview.Views.TFSProjectForReviewE, agregatorName, detailPath),
                languageDef.GetFunction(
                    languageDef.funcEQ,
                    new VariableDef(languageDef.GuidType, Information.ExtractPropertyName <TFSProjectForReview>(n => n.TFSProject)),
                    project.__PrimaryKey));

            lcs.LimitFunction = languageDef.GetFunction(languageDef.funcAND, dateLimit, projectLimit);

            return(DataServiceProvider.DataService.LoadObjects(lcs).Cast <TeamSlice>().ToList());
        }
        /// <summary>
        /// Обработать конкретный проект из TFS.
        /// Выполняет генерацию задач на проверку кода для указанного прокта.
        /// </summary>
        /// <param name="tfsProject">Проект из TFS для которого необходимо сгенерировать задачи на проверку кода.</param>
        private void ProcessProject(TFSProject tfsProject)
        {
            // В проекте необходимо указывать последнию дату проверки иначе она будет проставлена в текущий день.
            if (tfsProject.LastCheckDate == null)
            {
                tfsProject.LastCheckDate = new NullableDateTime {
                    Value = _now
                }
            }
            ;

            List <TeamSlice> teamSlices = GetTeamSlices(tfsProject);

            // Если нет команды в текущий период времени, то и CheckIn'ы нет смысла перебирать.
            if (teamSlices.Count == 0)
            {
                return;
            }

            string projectName  = tfsProject.GetProjectName();
            string tfsServerUrl = tfsProject.GetProjectCollectionUrl();

            var projectCollection = new TfsTeamProjectCollection(new Uri(tfsServerUrl));

            projectCollection.EnsureAuthenticated();

            // Получаем changeset'ы, начиная с даты последней проверки по настоящий момент.
            IEnumerable <Changeset> changesets = GetChangesets(projectCollection, projectName, tfsProject.LastCheckDate.Value, _now);

            if (projectName.Contains('/'))
            {
                projectName = projectName.Split('/')[0];
            }

            // Сервис для работы с пользователями tfs (будем получать пользователя по его логину полному).
            IIdentityManagementService identityManagementService = projectCollection.GetService <IIdentityManagementService>();

            foreach (Changeset changeset in changesets)
            {
                var creationDate = changeset.CreationDate;
                var teamSlice    = teamSlices.FirstOrDefault(n => n.StartDate <= creationDate && n.EndDate >= creationDate);

                if (teamSlice == null)
                {
                    throw new InvalidOperationException("Невозможно подобрать команду под указанный диапазон дат.");
                }

                var identity = identityManagementService.ReadIdentity(
                    IdentitySearchFactor.AccountName, changeset.Committer, MembershipQuery.None, ReadIdentityOptions.None);
                if (identity == null)
                {
                    throw new InvalidOperationException(
                              string.Format("Не удалось получить сведения по логину {0}.", changeset.Committer));
                }

                /* Проведение имперсонализации:
                 * создаём review request от имени того, кто коммитил (это позволит потом этому человеку закрыть review request).
                 * Чтобы всё корректно проходило, у пользователя, от имени которого работает данный сервис,
                 * должно быть разрешение “Make requests on behalf of others”.
                 */
                var impersonatedCollection = new TfsTeamProjectCollection(projectCollection.Uri, identity.Descriptor);
                impersonatedCollection.EnsureAuthenticated();
                WorkItemStore impersonatedWorkitemStore = impersonatedCollection.GetService <WorkItemStore>();
                Project       impersonatedProject       = impersonatedWorkitemStore.Projects[projectName];

                // Программист, которому будет назначен чекин на проверку.
                TFSProgrammer programmer = GetRandomProgrammer(teamSlice, changeset.CommitterDisplayName);
                if (programmer == null)
                {
                    throw new InvalidOperationException("Команда в подобранный диапазон дат вовсе не содержит программистов или содержит только автора набора изменений.");
                }

                var responseWorkItem = new WorkItem(impersonatedProject.WorkItemTypes[TFSCommonCollectionContsntans.ResponseWorkItemType])
                {
                    Title = CutWorkitemTitle(changeset.Comment)
                };
                responseWorkItem.Fields["System.AssignedTo"].Value    = programmer.Name;
                responseWorkItem.Fields["System.State"].Value         = TFSCommonCollectionContsntans.StateWorkItemCreation;
                responseWorkItem.Fields["System.Reason"].Value        = ReasonWorkItemCreation;
                responseWorkItem.Fields["System.AreaPath"].Value      = tfsProject.WorkItemAreaPath ?? projectName;
                responseWorkItem.Fields["System.IterationPath"].Value = tfsProject.WorkItemIterationPath ?? projectName;

                SaveWorkItem(responseWorkItem, changeset.ChangesetId);

                var requestWorkItem = new WorkItem(impersonatedProject.WorkItemTypes[TFSCommonCollectionContsntans.RequestWorkItemType])
                {
                    Title = CutWorkitemTitle(changeset.Comment)
                };
                requestWorkItem.Fields["System.AssignedTo"].Value = changeset.CommitterDisplayName;
                requestWorkItem.Fields["Microsoft.VSTS.CodeReview.ContextType"].Value = WorkItemContextTypeForChangeset;
                requestWorkItem.Fields["Microsoft.VSTS.CodeReview.Context"].Value     = changeset.ChangesetId;
                requestWorkItem.Fields["System.AreaPath"].Value      = tfsProject.WorkItemAreaPath ?? projectName;
                requestWorkItem.Fields["System.IterationPath"].Value = tfsProject.WorkItemIterationPath ?? projectName;
                requestWorkItem.Fields["System.State"].Value         = TFSCommonCollectionContsntans.StateWorkItemCreation;
                requestWorkItem.Fields["System.Reason"].Value        = ReasonWorkItemCreation;
                WorkItemLinkTypeEnd linkTypeEnd = impersonatedWorkitemStore.WorkItemLinkTypes.LinkTypeEnds[LinkChildWorkItem];
                requestWorkItem.Links.Add(new RelatedLink(linkTypeEnd, responseWorkItem.Id));

                SaveWorkItem(requestWorkItem, changeset.ChangesetId);

                // Актуализируем дату последней проверки проекта, проставляю в неё время последнего проверенного набора изменений.
                tfsProject.LastCheckDate = new NullableDateTime {
                    Value = changeset.CreationDate
                };
            }

            // В конце успешной проверки всех наборов изменений проставляем время последней проверки в текущее.
            tfsProject.LastCheckDate = new NullableDateTime {
                Value = _now
            };
        }