public virtual IRatingInfo GetRatingInfo()
        {
            if (RatingInfo != null)
            {
                return(RatingInfo);
            }

            var    ratings = SelectRatings();
            int    yesCount = 0, noCount = 0, totalCount = 0, ratingSum = 0;
            double averageRating = 0.0f;

            foreach (var rating in ratings)
            {
                var value = rating.Value;

                if (value == 0)
                {
                    noCount++;
                }
                if (value == 1)
                {
                    yesCount++;
                }

                totalCount++;

                ratingSum = ratingSum + value;

                averageRating = (double)ratingSum / (double)totalCount;
            }

            RatingInfo = new RatingInfo(yesCount, noCount, averageRating, totalCount, ratingSum);

            return(RatingInfo);
        }
        private static IEnumerable <Tuple <Guid, string, string, string, IRatingInfo> > FetchPageCommentExtendedData(
            OrganizationServiceContext serviceContext, Guid[] commentIds)
        {
            if (!FeatureCheckHelper.IsFeatureEnabled(FeatureNames.Feedback))
            {
                return(new List <Tuple <Guid, string, string, string, IRatingInfo> >());
            }
            if (!commentIds.Any())
            {
                return(Enumerable.Empty <Tuple <Guid, string, string, string, IRatingInfo> >());
            }

            var fetchXml = XDocument.Parse(@"
					<fetch mapping=""logical"">
						<entity name=""feedback"">
							<filter type=""and"">
							</filter>
							<link-entity link-type=""outer"" name=""contact"" from=""contactid"" to=""createdbycontact"" alias=""author"">
								<attribute name=""contactid"" />
								<attribute name=""fullname"" />
								<attribute name=""firstname"" />
								<attribute name=""lastname"" />
								<attribute name=""emailaddress1"" />
								<attribute name=""websiteurl"" />
							</link-entity>
						</entity>
					</fetch>"                    );

            var filter = fetchXml.Descendants("filter").First();

            filter.AddFetchXmlFilterInCondition(FeedbackMetadataAttributes.PageCommentIdAttribute,
                                                commentIds.Select(id => id.ToString()));

            var response = (RetrieveMultipleResponse)serviceContext.Execute(new RetrieveMultipleRequest
            {
                Query = new FetchExpression(fetchXml.ToString())
            });

            var aggregateFetchXml = XDocument.Parse(@"
				<fetch mapping=""logical"" aggregate=""true"">
					<entity name=""feedback"">
						<attribute name=""regardingobjectid"" alias=""ratingcount"" aggregate=""countcolumn""/>
						<attribute name=""rating"" alias=""ratingsum"" aggregate=""sum"" />
						<attribute name=""rating"" alias=""value"" groupby=""true"" />
						<link-entity name=""feedback"" from=""feedbackid"" to=""regardingobjectid"">
							<attribute name=""feedbackid"" alias=""commentid"" groupby=""true"" />
							<filter type=""and"" />
						</link-entity>
					</entity>
				</fetch>"                );


            var aggregateFilter = aggregateFetchXml.Descendants("filter").First();

            aggregateFilter.AddFetchXmlFilterInCondition(FeedbackMetadataAttributes.PageCommentIdAttribute,
                                                         commentIds.Select(id => id.ToString()));

            var aggregateResponse = (RetrieveMultipleResponse)serviceContext.Execute(new RetrieveMultipleRequest
            {
                Query = new FetchExpression(aggregateFetchXml.ToString())
            });

            return(commentIds.Select(id =>
            {
                var entity = response.EntityCollection.Entities.FirstOrDefault(e => e.Id == id);

                if (entity == null)
                {
                    return new Tuple <Guid, string, string, string, IRatingInfo>(id, null, null, null, null);
                }

                var authorName = Localization.LocalizeFullName(entity.GetAttributeAliasedValue <string>("author.firstname"), entity.GetAttributeAliasedValue <string>("author.lastname"));
                var authorUrl = entity.GetAttributeAliasedValue <string>("author.websiteurl");
                var authorEmail = entity.GetAttributeAliasedValue <string>("author.emailaddress1");

                var aggregateResults = aggregateResponse.EntityCollection.Entities
                                       .Where(e => e.GetAttributeAliasedValue <Guid?>("commentid") == id);

                var aggregateYesResult = aggregateResponse.EntityCollection.Entities
                                         .Where(e => e.GetAttributeAliasedValue <Guid?>("commentid") == id)
                                         .FirstOrDefault(e => e.GetAttributeAliasedValue <int?>("value") == 1);

                var aggregateNoResult = aggregateResponse.EntityCollection.Entities
                                        .Where(e => e.GetAttributeAliasedValue <Guid?>("commentid") == id)
                                        .FirstOrDefault(e => e.GetAttributeAliasedValue <int?>("value") == 0);

                var yesCount = (aggregateYesResult != null)
                                        ? aggregateYesResult.GetAttributeAliasedValue <int?>("ratingcount") ?? 0
                                        : 0;

                var noCount = (aggregateNoResult != null)
                                        ? aggregateNoResult.GetAttributeAliasedValue <int?>("ratingcount") ?? 0
                                        : 0;

                int ratingCount = 0;
                int ratingSum = 0;

                foreach (var aggregateResult in aggregateResults)
                {
                    ratingCount = ratingCount + aggregateResult.GetAttributeAliasedValue <int?>("ratingcount") ?? 0;
                    ratingSum = ratingSum + aggregateResult.GetAttributeAliasedValue <int?>("ratingsum") ?? 0;
                }

                double averageRating = 0;

                if (ratingCount == 0)
                {
                    averageRating = 0;
                }
                else
                {
                    averageRating = (double)ratingSum / (double)ratingCount;
                }

                var ratingInfo = new RatingInfo(yesCount, noCount, averageRating, ratingCount, ratingSum);

                return new Tuple <Guid, string, string, string, IRatingInfo>(id, authorName, authorUrl, authorEmail, ratingInfo);
            }));
        }