/// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            var restrictions = AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(restrictions.Item1) ||
                null == restrictions.Item3 || !restrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            // Set a expected level number as 2, and store it in the parameter expectedLevel.
            int    expectedLevels      = 2;
            string entitySetName       = restrictions.Item1;
            string entityTypeShortName = entitySetName.MapEntitySetNameToEntityTypeShortName();

            string[] navigPropNames = MetadataHelper.GetNavigPropNamesRecurseByLevels(entityTypeShortName, context.MetadataDocument, expectedLevels);
            string   url            = string.Format("{0}/{1}?$expand=*($levels={2})", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySetName, expectedLevels);
            var      resp           = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

            if (resp != null && resp.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);
                var entities = JsonParserHelper.GetEntries(feed);

                if (null == entities || !entities.Any())
                {
                    detail.ErrorMessage = string.Format("Cannot find any entities from the entity-set '{0}'", entitySetName);
                    info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                    return(passed);
                }

                bool    isFirstLevelEmpty = false;
                int     levelsCounter     = 0;
                JObject entry             = null;

                foreach (var en in entities)
                {
                    levelsCounter     = 0;
                    entry             = en as JObject;
                    isFirstLevelEmpty = false;
                    for (int i = 0; i < expectedLevels; i++)
                    {
                        if (entry != null && JTokenType.Object == entry.Type)
                        {
                            var navigPropVal = entry[navigPropNames[i]];

                            if (navigPropVal != null)
                            {
                                levelsCounter++;
                                entry = navigPropVal.Type == JTokenType.Array ? navigPropVal.First as JObject : navigPropVal as JObject;
                                if (entry == null)
                                {
                                    isFirstLevelEmpty = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (expectedLevels == levelsCounter)
                    {
                        passed = true;
                    }
                    else if (!(levelsCounter < expectedLevels && isFirstLevelEmpty)) // If not no data
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not execute an accurate result on the system query option '$levels' for expanded properties.";

                        break;
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the system query option '$levels' for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            var expandRestrictions           = AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(expandRestrictions.Item1) ||
                null == expandRestrictions.Item3 || !expandRestrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet = expandRestrictions.Item1;
            string navigProp = expandRestrictions.Item3.First().NavigationPropertyName;
            string url       = string.Format("{0}/{1}", context.ServiceBaseUri, entitySet);
            var    resp      = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                info = new ExtensionRuleViolationInfo(new Uri(url), resp.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);
            var entities = JsonParserHelper.GetEntries(feed);

            if (null == entities || 0 == entities.Count)
            {
                detail.ErrorMessage = string.Format("The entity-set {0} has no entity.", entitySet);
                info = new ExtensionRuleViolationInfo(new Uri(url), resp.ResponsePayload, detail);

                return(passed);
            }

            var    entity    = entities.First;
            string entityURL = null != entity[Constants.V4OdataId] ? entity[Constants.V4OdataId].ToString() : string.Empty;

            if (string.IsNullOrEmpty(entityURL))
            {
                detail.ErrorMessage = "Cannot find the annotation @odata.id in the current entity.";
                info = new ExtensionRuleViolationInfo(new Uri(url), resp.ResponsePayload, detail);

                return(passed);
            }

            url    = string.Format("{0}?$expand={1}/$ref", entityURL, navigProp);
            resp   = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

            if (resp.StatusCode == HttpStatusCode.OK)
            {
                JObject entry;
                resp.ResponsePayload.TryToJObject(out entry);

                if (entry != null && JTokenType.Object == entry.Type)
                {
                    entity = entry[navigProp].First;
                    url    = entity[Constants.V4OdataId].ToString();
                    resp   = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

                    if (resp.StatusCode == HttpStatusCode.OK)
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not execute an accurate result, because the value of the annotation '@odata.id' is a bad link.";
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the '$ref' segment for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            var restrictions = new Dictionary <string, Tuple <List <NormalProperty>, List <NavigProperty> > >();

            if (!AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (!AnnotationsHelper.IsSuitableNavigationProperty(NavigationRoughType.CollectionValued, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any collection-valued navigation properties in any entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet = string.Empty;
            string navigProp = string.Empty;

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                entitySet = r.Key;

                foreach (var np in r.Value.Item2)
                {
                    navigProp = np.NavigationPropertyName;
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    var    rest = AnnotationsHelper.GetCountRestrictions(context.MetadataDocument, context.VocCapabilities);

                    if (string.IsNullOrEmpty(rest.Item1) ||
                        null == rest.Item2 || !rest.Item2.Any() ||
                        null == rest.Item3 || !rest.Item3.Any())
                    {
                        continue;
                    }

                    break;
                }

                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigProp))
            {
                detail.ErrorMessage = "Cannot get expanded entities because cannot get collection type of navigation property from metadata";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string   url  = string.Format("{0}/{1}?$expand={2}($count=true)", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, navigProp);
            Uri      uri  = new Uri(url);
            Response resp = WebHelper.Get(uri, Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            detail                    = new ExtensionRuleResultDetail(this.Name, uri.AbsoluteUri, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);
            detail.URI                = url;
            detail.ResponsePayload    = resp.ResponsePayload;
            detail.ResponseHeaders    = resp.ResponseHeaders;
            detail.HTTPMethod         = "GET";
            detail.ResponseStatusCode = resp.StatusCode.ToString();
            if (resp != null && resp.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                resp.ResponsePayload.TryToJObject(out feed);

                if (null == feed)
                {
                    detail.ErrorMessage = "Cannot find any entity-sets in the service.\r\n";
                    info = new ExtensionRuleViolationInfo(uri, resp.ResponsePayload, detail);

                    return(passed);
                }

                JObject entry    = null;
                var     entities = JsonParserHelper.GetEntries(feed);

                foreach (var e in entities)
                {
                    if (null != e[navigProp] && JTokenType.Array == e[navigProp].Type)
                    {
                        entry = e as JObject;
                        break;
                    }
                }

                if (null == entry)
                {
                    detail.ErrorMessage = "Cannot find an appropriate entity which contains at least one collection-valued navigation property.\r\n";
                    info = new ExtensionRuleViolationInfo(uri, resp.ResponsePayload, detail);

                    return(passed);
                }


                if (null != entry && JTokenType.Object == entry.Type)
                {
                    JArray jArr         = entry[navigProp] as JArray;
                    int    actualAmount = 0;
                    JsonParserHelper.GetEntitiesNumFromCollectionValuedNavigProp(entry[Constants.V4OdataId].ToString(), entry, navigProp, context.RequestHeaders, ref actualAmount);

                    if (null != entry[navigProp + Constants.V4OdataCount] && actualAmount == (int)entry[navigProp + Constants.V4OdataCount])
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = "The service does not execute an accurate result on the system query option '$count' for expanded properties.";
                    }
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the system query option '$count' for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?         passed  = null;
            List <string> vocDocs = new List <string>()
            {
                context.VocCapabilities, context.VocCore, context.VocMeasures
            };
            List <string> expectedTypes = new List <string>()
            {
                "Edm.String", "Edm.Int32", "Edm.Int16", "Edm.Single", "Edm.Double", "Edm.Boolean", "Edm.DateTimeOffset", "Edm.Guid"
            };
            List <ExtensionRuleResultDetail> details = new List <ExtensionRuleResultDetail>();
            ExtensionRuleResultDetail        detail  = new ExtensionRuleResultDetail(this.Name);

            var restrictions = new Dictionary <string, Tuple <List <NormalProperty>, List <NavigProperty> > >();

            if (!AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (!AnnotationsHelper.IsSuitableNavigationProperty(NavigationRoughType.CollectionValued, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any collection-valued navigation properties in any entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet             = string.Empty;
            string navigPropName         = string.Empty;
            string primitivePropertyName = string.Empty;
            string primitivePropertyType = string.Empty;

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    string nEntitySetName       = nEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();
                    var    funcs = new List <Func <string, string, string, List <NormalProperty>, List <NavigProperty>, bool> >()
                    {
                        AnnotationsHelper.GetFilterRestrictions
                    };
                    var rest = nEntitySetName.GetRestrictions(context.MetadataDocument, context.VocCapabilities, funcs, expectedTypes);

                    if (string.IsNullOrEmpty(rest.Item1) ||
                        null == rest.Item2 || !rest.Item2.Any() ||
                        null == rest.Item3 || !rest.Item3.Any())
                    {
                        continue;
                    }

                    navigPropName         = np.NavigationPropertyName;
                    primitivePropertyName = rest.Item2.First().PropertyName;
                    primitivePropertyType = rest.Item2.First().PropertyType;

                    break;
                }

                entitySet = r.Key;

                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigPropName))
            {
                detail.ErrorMessage = "Cannot get expanded entities because cannot get collection type of navigation property from metadata";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (string.IsNullOrEmpty(primitivePropertyName) || string.IsNullOrEmpty(primitivePropertyType))
            {
                detail.ErrorMessage = "Cannot get an appropriate primitive property from navigation properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }
            string struri   = string.Format("{0}/{1}?$expand={2}", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, navigPropName);
            Uri    uri      = new Uri(struri);
            var    response = WebHelper.Get(uri, Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (HttpStatusCode.OK != response.StatusCode)
            {
                passed = false;
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(response.ResponsePayload);
                info = new ExtensionRuleViolationInfo(uri, response.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            response.ResponsePayload.TryToJObject(out feed);

            if (feed != null && JTokenType.Object == feed.Type)
            {
                var entities  = JsonParserHelper.GetEntries(feed);
                var navigProp = entities[0][navigPropName] as JArray;

                if (navigProp != null && navigProp.Count != 0)
                {
                    string propVal    = navigProp[0][primitivePropertyName].ToString();
                    string compareVal = propVal;

                    if (primitivePropertyType.Equals("Edm.String"))
                    {
                        compareVal = "'" + propVal + "'";
                    }

                    string url  = string.Format("{0}/{1}?$expand={2}($filter={3} eq {4})", context.ServiceBaseUri.OriginalString.TrimEnd('/'), entitySet, navigPropName, primitivePropertyName, compareVal);
                    var    resp = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
                    detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), resp);

                    if (resp.StatusCode == HttpStatusCode.OK)
                    {
                        JObject jObj;
                        resp.ResponsePayload.TryToJObject(out jObj);

                        if (jObj != null && JTokenType.Object == jObj.Type)
                        {
                            var entries = JsonParserHelper.GetEntries(jObj).ToList();

                            foreach (var entry in entries)
                            {
                                if (entry[navigPropName] != null && ((JArray)entry[navigPropName]).Count == 0)
                                {
                                    continue;
                                }
                                else if (entry[navigPropName] != null && ((JArray)entry[navigPropName]).Count > 0)
                                {
                                    var temp = entry[navigPropName].ToList()
                                               .FindAll(en => propVal == en[primitivePropertyName].ToString())
                                               .Select(en => en);

                                    if (entry[navigPropName].ToList().Count == temp.Count())
                                    {
                                        passed = true;
                                    }
                                    else
                                    {
                                        passed = false;
                                        detail.ErrorMessage = string.Format("The service does not execute an accurate result on system query option '$filter' (Actual Value: {0}, Expected Value: {1}).", entry[navigPropName].ToList().Count, temp.Count());
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = string.Format("The service does not support system query option '$filter' on expanded entities.");
                    }
                }
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            var restrictions = new Dictionary <string, Tuple <List <NormalProperty>, List <NavigProperty> > >();

            if (!AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (!AnnotationsHelper.IsSuitableNavigationProperty(NavigationRoughType.CollectionValued, ref restrictions))
            {
                detail.ErrorMessage = "Cannot find any collection-valued navigation properties in any entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string entitySet             = string.Empty;
            string navigPropName         = string.Empty;
            string primitivePropertyName = string.Empty;

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityType = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    var    funcs       = new List <Func <string, string, string, List <NormalProperty>, List <NavigProperty>, bool> >()
                    {
                        AnnotationsHelper.GetFilterRestrictions
                    };
                    var expectedTypes = new List <string>()
                    {
                        "Edm.String"
                    };
                    var props = MetadataHelper.GetNormalProperties(context.MetadataDocument, nEntityType);

                    if (null == props || !props.Any())
                    {
                        continue;
                    }

                    var targetProps = props.Where(p => expectedTypes.Contains(p.PropertyType)).Select(p => p);

                    if (!targetProps.Any())
                    {
                        continue;
                    }

                    navigPropName         = np.NavigationPropertyName;
                    primitivePropertyName = targetProps.First().PropertyName;

                    break;
                }

                entitySet = r.Key;

                if (!string.IsNullOrEmpty(navigPropName) && !string.IsNullOrEmpty(primitivePropertyName))
                {
                    break;
                }
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigPropName))
            {
                detail.ErrorMessage = "Cannot get expanded entities because cannot get collection type of navigation property from metadata";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            if (string.IsNullOrEmpty(primitivePropertyName))
            {
                detail.ErrorMessage = "Cannot get an appropriate primitive property from navigation properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string url  = string.Format("{0}/{1}?$expand={2}", context.ServiceBaseUri, entitySet, navigPropName);
            var    resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);

            if (null == resp || HttpStatusCode.OK != resp.StatusCode)
            {
                detail.ErrorMessage = JsonParserHelper.GetErrorMessage(resp.ResponsePayload);
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            JObject feed;

            resp.ResponsePayload.TryToJObject(out feed);
            var entities = JsonParserHelper.GetEntries(feed);

            if (null == entities || !entities.Any())
            {
                detail.ErrorMessage = string.Format("Cannot find any entities from the entity-set '{0}'", entitySet);
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            string searchVal = string.Empty;

            foreach (var en in entities)
            {
                if (null != en[navigPropName] || JTokenType.Array == en[navigPropName].Type || en[navigPropName].Any())
                {
                    if (JTokenType.Object != en[navigPropName].First.Type)
                    {
                        break;
                    }

                    var nEntity = en[navigPropName].First as JObject;
                    searchVal = nEntity[primitivePropertyName].ToString().Contains(" ") ?
                                string.Format("\"{0}\"", nEntity[primitivePropertyName].ToString()) :
                                nEntity[primitivePropertyName].ToString();

                    if (!string.IsNullOrEmpty(searchVal))
                    {
                        break;
                    }
                }
            }

            if (string.IsNullOrEmpty(searchVal))
            {
                detail.ErrorMessage = "Cannot find any appropriate search values in the expanded entities.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return(passed);
            }

            url    = string.Format("{0}/{1}?$expand={2}($search={3})", context.ServiceBaseUri, entitySet, navigPropName, searchVal);
            resp   = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", String.Empty, resp);

            if (null != resp && resp.StatusCode == HttpStatusCode.OK)
            {
                resp.ResponsePayload.TryToJObject(out feed);

                if (null != feed && JTokenType.Object == feed.Type)
                {
                    entities = JsonParserHelper.GetEntries(feed);

                    foreach (var e in entities)
                    {
                        var navigEntities = e[navigPropName].ToList();

                        if (null == navigEntities || !navigEntities.Any())
                        {
                            continue;
                        }

                        foreach (var ne in navigEntities)
                        {
                            passed = null;

                            if (searchVal.StripOffDoubleQuotes() == ne[primitivePropertyName].ToString())
                            {
                                passed = true;
                            }

                            if (passed == null)
                            {
                                passed = false;
                                detail.ErrorMessage = "The service does not execute an accurate result on the system query option '$search' for expanded properties.";
                            }
                        }

                        if (passed == false)
                        {
                            break;
                        }
                    }

                    if (null == passed)
                    {
                        detail.ErrorMessage = "Cannot find any appropriate data to verify this rule.";
                    }
                }
                else
                {
                    detail.ErrorMessage = "The service does not return an correct response payload.";
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support the system query option '$search' for expanded properties.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return(passed);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool?Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool?passed = null;

            List <ExtensionRuleResultDetail> details = new List <ExtensionRuleResultDetail>();
            ExtensionRuleResultDetail        detail1 = new ExtensionRuleResultDetail(this.Name);
            ExtensionRuleResultDetail        detail2 = new ExtensionRuleResultDetail(this.Name);
            var restrictions = new Dictionary <string, Tuple <List <NormalProperty>, List <NavigProperty> > >();

            if (!AnnotationsHelper.GetExpandRestrictions(context.MetadataDocument, context.VocCapabilities, ref restrictions))
            {
                detail1.ErrorMessage = "Cannot find any appropriate entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            if (!AnnotationsHelper.IsSuitableNavigationProperty(NavigationRoughType.CollectionValued, ref restrictions))
            {
                detail1.ErrorMessage = "Cannot find any collection-valued navigation properties in any entity-sets which supports $expand system query options.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            string entitySet     = string.Empty;
            string navigPropName = string.Empty;

            #region Verify the system query $top.

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    string nEntitySetName       = nEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();

                    if (false == nEntitySetName.IsEntitySetSupportTopQuery(context.MetadataDocument, new List <string>()
                    {
                        context.VocCapabilities
                    }))
                    {
                        continue;
                    }

                    navigPropName = np.NavigationPropertyName;
                    break;
                }

                entitySet = r.Key;
                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail1.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigPropName))
            {
                detail1.ErrorMessage = "Cannot find any collection-valued navigation properties which support system query options $top.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            bool?  isTopQueryValidation = null;
            string url      = string.Format("{0}/{1}?$expand={2}($top=1)", context.ServiceBaseUri, entitySet, navigPropName);
            var    response = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail1 = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), response);

            if (response.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                response.ResponsePayload.TryToJObject(out feed);

                if (feed != null && JTokenType.Object == feed.Type)
                {
                    var entities = JsonParserHelper.GetEntries(feed);

                    foreach (var entity in entities)
                    {
                        if (entity[navigPropName] != null)
                        {
                            if (JTokenType.Array == entity[navigPropName].Type && ((JArray)entity[navigPropName]).Count <= 1)
                            {
                                isTopQueryValidation = true;
                            }
                            else
                            {
                                passed = false;
                                detail1.ErrorMessage = "The service does not execute an accurate result on the system query option '$top' for expanded properties.";
                                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                                return(passed);
                            }
                        }
                    }
                }
            }
            else
            {
                passed = false;
                detail1.ErrorMessage = "The service does not support the system query option '$top' for expanded properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            #endregion

            #region Verify the system query $skip.

            foreach (var r in restrictions)
            {
                if (string.IsNullOrEmpty(r.Key) ||
                    null == r.Value.Item1 || !r.Value.Item1.Any() ||
                    null == r.Value.Item2 || !r.Value.Item2.Any())
                {
                    continue;
                }

                foreach (var np in r.Value.Item2)
                {
                    string nEntityTypeShortName = np.NavigationPropertyType.RemoveCollectionFlag().GetLastSegment();
                    string nEntitySetName       = nEntityTypeShortName.MapEntityTypeShortNameToEntitySetName();

                    if (false == nEntitySetName.IsEntitySetSupportSkipQuery(context.MetadataDocument, new List <string>()
                    {
                        context.VocCapabilities
                    }))
                    {
                        continue;
                    }

                    navigPropName = np.NavigationPropertyName;
                    break;
                }

                entitySet = r.Key;
                break;
            }

            if (string.IsNullOrEmpty(entitySet))
            {
                detail2.ErrorMessage = "Cannot find an appropriate entity-set which supports $expand system query option.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            if (string.IsNullOrEmpty(navigPropName))
            {
                detail2.ErrorMessage = "Cannot find any collection-valued navigation properties which support system query options $skip.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            bool?isSkipQueryValidation = null;
            url      = string.Format("{0}/{1}?$expand={2}($skip=1)", context.ServiceBaseUri, entitySet, navigPropName);
            response = WebHelper.Get(new Uri(url), Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            detail2  = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), response);

            if (response.StatusCode == HttpStatusCode.OK)
            {
                JObject feed;
                response.ResponsePayload.TryToJObject(out feed);

                if (feed != null && JTokenType.Object == feed.Type)
                {
                    var entities = JsonParserHelper.GetEntries(feed);

                    foreach (var entity in entities)
                    {
                        if (entity[navigPropName] != null)
                        {
                            int actualAmount = 0;
                            JsonParserHelper.GetEntitiesNumFromCollectionValuedNavigProp(context.DestinationBasePath, (JObject)entity, navigPropName, context.RequestHeaders, ref actualAmount);

                            if (JTokenType.Array == entity[navigPropName].Type && ((JArray)entity[navigPropName]).Count <= actualAmount)// TODO: This should be justified by total count
                            {
                                isSkipQueryValidation = true;
                            }
                            else
                            {
                                isSkipQueryValidation = false;
                                detail2.ErrorMessage  = "The service does not execute an accurate result on the system query option '$skip' for expanded properties.";
                                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                                return(isSkipQueryValidation);
                            }
                        }
                    }
                }
            }
            else
            {
                passed = false;
                detail2.ErrorMessage = "The service does not support the system query option '$skip' for expanded properties.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);

                return(passed);
            }

            #endregion

            if (isTopQueryValidation == true && isSkipQueryValidation == true)
            {
                passed = true;
            }

            details.Add(detail1);
            details.Add(detail2);
            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, details);

            return(passed);
        }