예제 #1
0
        private static string GetExpandedSelect(FetchLinkEntityType linkitem, string relation, FetchXmlBuilder sender)
        {
            if (linkitem.Items == null)
            {
                return("");
            }
            var resultList     = new List <string>();
            var linkentity     = linkitem.name;
            var attributeitems = linkitem.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList();

            if (linkitem.intersect)
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    linkentity     = nextlink.name;
                    attributeitems = nextlink.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList();
                }
            }
            if (attributeitems.Count > 0)
            {
                foreach (FetchAttributeType attributeitem in attributeitems)
                {
                    resultList.Add(relation + "/" + LogicalToSchemaName(linkentity, attributeitem.name, sender));
                }
            }
            return(string.Join(",", resultList));
        }
        private static void removeUnnecessaryLinksAttributes(savedqueryLayoutxmlGrid viewFeo, ViewExcelSheetsInfo excelData)
        {
            FetchEntityType   currentEt = excelData.fetchObj.Items.Where(x => x is FetchEntityType).Select(x => (FetchEntityType)x).FirstOrDefault();
            ViewsRelationsObj obj       = excelData.relationsList.Where(x => x.relationAlias == null).FirstOrDefault();

            if (currentEt != null && obj != null)
            {
                List <object> array = currentEt.Items.ToList();
                for (int i = array.Count - 1; i > -1; i--)
                {
                    object currentItem = array[i];
                    if (currentItem is FetchLinkEntityType)
                    {
                        FetchLinkEntityType link = currentItem as FetchLinkEntityType;
                        removeUnnecessaryAttributesInner(link, viewFeo);
                        if (link == null || link.visible == false)
                        {
                            if (link == null || link.Items.Count() == 0)
                            {
                                array.RemoveAt(i);
                            }
                        }
                    }
                    else if (currentItem is FetchAttributeType)
                    {
                        FetchAttributeType attr = currentItem as FetchAttributeType;
                        if (!(viewFeo.row.cell.Where(x => x.name == attr.name).Count() > 0) && attr.name != obj.entityMetadata.PrimaryIdAttribute)
                        {
                            array.RemoveAt(i);
                        }
                    }
                }
                currentEt.Items = array.ToArray();
            }
        }
        private static List <string> GetExpandedSelect(FetchLinkEntityType linkitem, string relation)
        {
            var resultList = new List <string>();

            if (linkitem.Items != null)
            {
                var attributeitems = linkitem.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList();
                if (linkitem.intersect)
                {
                    var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                    if (linkitems.Count > 1)
                    {
                        throw new Exception("Invalid M:M-relation definition for OData");
                    }
                    if (linkitems.Count == 1)
                    {
                        var nextlink = (FetchLinkEntityType)linkitems[0];
                        attributeitems = nextlink.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList();
                    }
                }
                if (attributeitems.Count > 0)
                {
                    foreach (FetchAttributeType attributeitem in attributeitems)
                    {
                        resultList.Add(relation + "." + attributeitem.name);
                    }
                }
            }
            return(resultList);
        }
예제 #4
0
        private static string GetExpandedSelect(FetchLinkEntityType linkitem, FetchXmlBuilder sender, string entityname = "")
        {
            if (linkitem.Items == null)
            {
                return(null);
            }

            var linkentity = linkitem.name;

            if (linkentity == null)
            {
                return(null);
            }

            var attributeitems = linkitem.Items
                                 .OfType <FetchAttributeType>()
                                 .Where(i => i.name != null);

            if (linkitem.intersect)
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    linkentity = nextlink.name;

                    if (nextlink.Items == null)
                    {
                        return(null);
                    }

                    attributeitems = nextlink.Items
                                     .OfType <FetchAttributeType>()
                                     .Where(i => i.name != null);
                }
            }

            var resultList     = GetAttributeNames(linkentity, attributeitems, sender);
            var expandedSelect = string.Join(",", resultList);
            var nestedExpand   = GetNestedExpand(linkitem, sender, linkitem.name);

            var options = new List <string>();

            if (!String.IsNullOrEmpty(expandedSelect))
            {
                options.Add("$select=" + expandedSelect);
            }

            if (!String.IsNullOrEmpty(nestedExpand))
            {
                options.Add("$expand=" + nestedExpand);
            }

            return(String.Join(";", options));
        }
        private static void updateViewRelatedObj(FetchType fetchType, string attributeName, List <ViewsRelationsObj> relationObj)
        {
            int index = attributeName.IndexOf(".");

            if (index == -1)
            {
                if (relationObj.Where(x => x.relationAlias == null).Count() == 0)
                {
                    foreach (object fetch in fetchType.Items)
                    {
                        if (fetch is FetchEntityType)
                        {
                            foreach (object obj in ((FetchEntityType)fetch).Items)
                            {
                                if (obj is FetchAttributeType && attributeName == ((FetchAttributeType)obj).name)
                                {
                                    FetchEntityType fetchent = (FetchEntityType)fetch;
                                    relationObj.Add(new ViewsRelationsObj()
                                    {
                                        entity         = fetchent.name,
                                        entityMetadata = GlobalOperations.Instance.CRMOpHelper.RetriveEntityAtrribute(fetchent.name)
                                    });
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                string alias = attributeName.Substring(0, index);
                if (relationObj.Where(x => x.relationAlias != null && attributeName.StartsWith(x.relationAlias)).Count() == 0)
                {
                    foreach (object fetch in fetchType.Items)
                    {
                        foreach (object obj in ((FetchEntityType)fetch).Items)
                        {
                            if (obj is FetchLinkEntityType)
                            {
                                FetchLinkEntityType relation = getFechXmlFromLinks((FetchLinkEntityType)obj, attributeName);
                                if (relation != null)
                                {
                                    relationObj.Add(new ViewsRelationsObj()
                                    {
                                        entity         = relation.name,
                                        entityMetadata = GlobalOperations.Instance.CRMOpHelper.RetriveEntityAtrribute(relation.name),
                                        relationAlias  = relation.alias,
                                        relationFrom   = relation.from,
                                        relationTo     = relation.to
                                    });
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #6
0
        private void RemoveAttributesAndOrders(FetchLinkEntityType entity)
        {
            if (entity.Items != null)
            {
                entity.Items = entity.Items.Where(o => !(o is FetchAttributeType) && !(o is allattributes) && !(o is FetchOrderType)).ToArray();

                foreach (var linkEntity in entity.Items.OfType<FetchLinkEntityType>())
                    RemoveAttributesAndOrders(linkEntity);
            }
        }
예제 #7
0
 public static void AddItem(this FetchLinkEntityType linkEntity, object item)
 {
     if (linkEntity.Items == null)
     {
         linkEntity.Items = new[] { item }
     }
     ;
     else
     {
         linkEntity.Items = linkEntity.Items.Concat(new[] { item }).ToArray();
     }
 }
        private static string GetExpandedSelect(FetchLinkEntityType linkitem, FetchXmlBuilder sender)
        {
            if (linkitem.Items == null)
            {
                return("");
            }

            var linkentity = linkitem.name;

            if (linkentity == null)
            {
                return(null);
            }

            var attributeitems = linkitem.Items
                                 .OfType <FetchAttributeType>()
                                 .Where(i => i.name != null);

            if (linkitem.intersect)
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    linkentity = nextlink.name;

                    if (nextlink.Items == null)
                    {
                        return(null);
                    }

                    attributeitems = nextlink.Items
                                     .OfType <FetchAttributeType>()
                                     .Where(i => i.name != null);
                }
            }

            var resultList = GetAttributeNames(linkentity, attributeitems, sender);

            return(string.Join(",", resultList));
        }
 private static void removeUnnecessaryAttributesInner(FetchLinkEntityType link, savedqueryLayoutxmlGrid viewFeo)
 {
     if (link.Items != null)
     {
         List <object> array = link.Items.ToList();
         for (int i = array.Count - 1; i > -1; i--)
         {
             object currentItem = array[i];
             if (currentItem is FetchAttributeType)
             {
                 FetchAttributeType attr  = currentItem as FetchAttributeType;
                 string             alias = string.Format("{0}.{1}", link.alias, attr.name);
                 if (!(viewFeo.row.cell.Where(x => x.name == attr.name).Count() > 0))
                 {
                     array.RemoveAt(i);
                 }
             }
         }
         link.Items = array.ToArray();
     }
 }
 private static FetchLinkEntityType getFechXmlFromLinks(FetchLinkEntityType fetchLinkType, string attributeName)
 {
     if (attributeName.Contains("."))
     {
         if (attributeName.StartsWith(string.Concat(((FetchLinkEntityType)fetchLinkType).alias)))
         {
             return((FetchLinkEntityType)fetchLinkType);
         }
         if (fetchLinkType != null && fetchLinkType.Items != null && fetchLinkType.Items.Count() > 0)
         {
             foreach (object fetchType in fetchLinkType.Items)
             {
                 if (fetchType is FetchLinkEntityType)
                 {
                     return(getFechXmlFromLinks((FetchLinkEntityType)fetchType, attributeName));
                 }
             }
         }
     }
     return(null);
 }
예제 #11
0
        private static string GetNestedExpand(FetchLinkEntityType linkitem, FetchXmlBuilder sender, string entityname)
        {
            var resultList = new List <string>();
            var linkitems  = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();

            if (linkitem.intersect || linkitems.Count == 0)
            {
                return("");
            }
            foreach (FetchLinkEntityType linkentityitem in linkitems)
            {
                var navigationProperty = LinkItemToNavigationProperty(entityname, linkentityitem, sender, out var child, out var manyToManyNextLink);
                var expandedSelect     = GetExpandedSelect(linkentityitem, sender, linkentityitem.name);
                if (String.IsNullOrEmpty(expandedSelect))
                {
                    resultList.Add(navigationProperty);
                }
                else
                {
                    resultList.Add(navigationProperty + "(" + expandedSelect + ")");
                }
            }
            return(string.Join(",", resultList));
        }
예제 #12
0
        private static FetchAttributeType FindAliasedAttribute(object[] items, string colName, Func <FetchAttributeType, bool> predicate, ref FetchLinkEntityType linkEntity)
        {
            if (items == null)
            {
                return(null);
            }

            var match = items.OfType <FetchAttributeType>()
                        .Where(a => a.alias == colName && (predicate == null || predicate(a)))
                        .FirstOrDefault();

            if (match != null)
            {
                return(match);
            }

            foreach (var le in items.OfType <FetchLinkEntityType>())
            {
                match = FindAliasedAttribute(le.Items, colName, predicate, ref linkEntity);

                if (match != null)
                {
                    if (linkEntity == null)
                    {
                        linkEntity = le;
                    }

                    return(match);
                }
            }

            return(null);
        }
예제 #13
0
        // ReSharper disable once UnusedParameter.Local
        private static IEnumerable <FetchAttributeInfo> GetAllAttributes(string entityLogicalName, FetchLinkEntityType link)
        {
            var items = link.Items;

            if (items == null)
            {
                return(new List <FetchAttributeInfo>());
            }

            return((from item in link.Items
                    select(IEnumerable <FetchAttributeInfo>) GetAllAttributes(link.name, (dynamic)item)).SelectMany(v => v as FetchAttributeInfo[] ?? v.ToArray()));
        }
예제 #14
0
        // ReSharper disable once UnusedParameter.Local
        private static void ProcessFetchXmlItem(LocalCrmDatabaseOrganizationService service, LinkEntity entityLink, FetchLinkEntityType link)
        {
            var joinType = link.linktype == "outer" ? JoinOperator.LeftOuter : JoinOperator.Inner;

            var childLink = new LinkEntity(entityLink.LinkToEntityName, link.name, link.@from, link.to, joinType)
            {
                EntityAlias = link.alias,
            };

            entityLink.LinkEntities.Add(childLink);
            var items = link.Items;

            if (items == null)
            {
                return;
            }

            foreach (dynamic item in items)
            {
                ProcessFetchXmlItem(service, childLink, item);
            }
        }
예제 #15
0
        internal FetchAttributeType AddAttribute(string colName, Func <FetchAttributeType, bool> predicate, IAttributeMetadataCache metadata, out bool added, out FetchLinkEntityType linkEntity)
        {
            var parts = colName.Split('.');

            if (parts.Length == 1)
            {
                added = false;
                return(Entity.FindAliasedAttribute(colName, predicate, out linkEntity));
            }

            var entityName = parts[0];
            var attr       = new FetchAttributeType {
                name = parts[1].ToLowerInvariant()
            };

            if (Alias == entityName)
            {
                linkEntity = null;

                var meta = metadata[Entity.name].Attributes.SingleOrDefault(a => a.LogicalName == attr.name && a.AttributeOf == null);
                if (meta == null && (attr.name.EndsWith("name") || attr.name.EndsWith("type")))
                {
                    var logicalName = attr.name.Substring(0, attr.name.Length - 4);
                    meta = metadata[Entity.name].Attributes.SingleOrDefault(a => a.LogicalName == logicalName && a.AttributeOf == null);

                    if (meta != null)
                    {
                        attr.name = logicalName;
                    }
                }

                if (Entity.Items != null)
                {
                    var existing = Entity.Items.OfType <FetchAttributeType>().FirstOrDefault(a => a.name == attr.name || a.alias == attr.name);
                    if (existing != null && (predicate == null || predicate(existing)))
                    {
                        added = false;
                        return(existing);
                    }
                }

                Entity.AddItem(attr);
            }
            else
            {
                linkEntity = Entity.FindLinkEntity(entityName);

                var meta = metadata[linkEntity.name].Attributes.SingleOrDefault(a => a.LogicalName == attr.name && a.AttributeOf == null);
                if (meta == null && (attr.name.EndsWith("name") || attr.name.EndsWith("type")))
                {
                    var logicalName = attr.name.Substring(0, attr.name.Length - 4);
                    meta = metadata[linkEntity.name].Attributes.SingleOrDefault(a => a.LogicalName == logicalName && a.AttributeOf == null);

                    if (meta != null)
                    {
                        attr.name = logicalName;
                    }
                }

                if (linkEntity.Items != null)
                {
                    var existing = linkEntity.Items.OfType <FetchAttributeType>().FirstOrDefault(a => a.name == attr.name || a.alias == attr.name);
                    if (existing != null && (predicate == null || predicate(existing)))
                    {
                        added = false;
                        return(existing);
                    }
                }

                linkEntity.AddItem(attr);
            }

            added = true;
            return(attr);
        }
예제 #16
0
        private static RelationshipMetadataBase LinkItemToRelation(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender)
        {
            GetEntityMetadata(entityname, sender);
            var entity = FetchXmlBuilder.entities[entityname];

            foreach (var relation in entity.OneToManyRelationships)
            {
                if (relation.ReferencedEntity == entityname &&
                    relation.ReferencedAttribute == linkitem.to &&
                    relation.ReferencingEntity == linkitem.name &&
                    relation.ReferencingAttribute == linkitem.from)
                {
                    return(relation);
                }
            }
            foreach (var relation in entity.ManyToOneRelationships)
            {
                if (relation.ReferencingEntity == entityname &&
                    relation.ReferencingAttribute == linkitem.to &&
                    relation.ReferencedEntity == linkitem.name &&
                    relation.ReferencedAttribute == linkitem.from)
                {
                    return(relation);
                }
            }
            foreach (var relation in entity.ManyToManyRelationships)
            {
                if (relation.Entity1LogicalName == entityname &&
                    relation.Entity1IntersectAttribute == linkitem.from)
                {
                    var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                    if (linkitems.Count > 1)
                    {
                        throw new Exception("Invalid M:M-relation definition for OData");
                    }
                    if (linkitems.Count == 1)
                    {
                        var nextlink = (FetchLinkEntityType)linkitems[0];
                        if (nextlink.linktype == "outer")
                        {
                            throw new Exception("OData queries do not support outer joins");
                        }
                        if (relation.Entity2LogicalName == nextlink.name &&
                            relation.Entity2IntersectAttribute == nextlink.to)
                        {
                            return(relation);
                        }
                    }
                }
                if (relation.Entity2LogicalName == entityname &&
                    relation.Entity2IntersectAttribute == linkitem.from)
                {
                    var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                    if (linkitems.Count > 1)
                    {
                        throw new Exception("Invalid M:M-relation definition for OData");
                    }
                    if (linkitems.Count == 1)
                    {
                        var nextlink = (FetchLinkEntityType)linkitems[0];
                        if (nextlink.linktype == "outer")
                        {
                            throw new Exception("OData queries do not support outer joins");
                        }
                        if (relation.Entity1LogicalName == nextlink.name &&
                            relation.Entity1IntersectAttribute == nextlink.to)
                        {
                            return(relation);
                        }
                    }
                }
            }
            throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}");
        }
 private static void addNewAttributeToFech(FetchType fecthObj, string attributeName, ViewsRelationsObj currentEntity)
 {
     if (string.IsNullOrEmpty(currentEntity.relationAlias))
     {
         FetchEntityType currentEt = fecthObj.Items.Where(x => x is FetchEntityType && ((FetchEntityType)x).name.Equals(currentEntity.entity, StringComparison.InvariantCultureIgnoreCase)).Select(x => (FetchEntityType)x).FirstOrDefault();
         if (currentEt != null)
         {
             List <object>      items     = currentEt.Items.ToList();
             FetchAttributeType attribute = items.Where(x => x is FetchAttributeType && ((FetchAttributeType)x).name.Equals(attributeName)).Select(x => (FetchAttributeType)x).FirstOrDefault();
             if (attribute == null)
             {
                 List <object> objectList = currentEt.Items.ToList();
                 objectList.Add(new FetchAttributeType()
                 {
                     name = attributeName
                 });
                 currentEt.Items = objectList.ToArray();
             }
         }
     }
     else
     {
         FetchEntityType currentEt = fecthObj.Items.Where(x => x is FetchEntityType).Select(x => (FetchEntityType)x).FirstOrDefault();
         if (currentEt != null)
         {
             FetchLinkEntityType currentLink = currentEt.Items.Where(x => x is FetchLinkEntityType &&
                                                                     ((FetchLinkEntityType)x).alias.Equals(currentEntity.relationAlias, StringComparison.InvariantCultureIgnoreCase))
                                               .Select(x => (FetchLinkEntityType)x).FirstOrDefault();
             if (currentLink != null)
             {
                 List <object>      items     = currentLink.Items.ToList();
                 FetchAttributeType attribute = items.Where(x => x is FetchAttributeType && ((FetchAttributeType)x).name.Equals(attributeName)).Select(x => (FetchAttributeType)x).FirstOrDefault();
                 if (attribute == null)
                 {
                     List <object> objectList = currentLink.Items.ToList();
                     objectList.Add(new FetchAttributeType()
                     {
                         name = attributeName
                     });
                     currentLink.Items = objectList.ToArray();
                 }
             }
             else
             {
                 List <object> objectList = currentEt.Items.ToList();
                 objectList.Add(new FetchLinkEntityType()
                 {
                     Items              = new object[] { (new FetchAttributeType()
                         {
                             name = attributeName
                         }) },
                     name               = currentEntity.entity,
                     alias              = currentEntity.relationAlias,
                     linktype           = "outer",
                     from               = currentEntity.relationFrom,
                     to                 = currentEntity.relationTo,
                     intersect          = false,
                     intersectSpecified = false,
                     visible            = false,
                     visibleSpecified   = false,
                 });
                 currentEt.Items = objectList.ToArray();
             }
         }
     }
 }
        private static string LinkItemToNavigationProperty(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender, out bool child, out FetchLinkEntityType manyToManyNextLink)
        {
            manyToManyNextLink = null;
            GetEntityMetadata(entityname, sender);
            var entity = sender.entities[entityname];

            foreach (var relation in entity.OneToManyRelationships
                     .Where(r =>
                            r.ReferencedEntity == entityname &&
                            r.ReferencedAttribute == linkitem.to &&
                            r.ReferencingEntity == linkitem.name &&
                            r.ReferencingAttribute == linkitem.from))
            {
                child = true;
                return(relation.ReferencedEntityNavigationPropertyName);
            }
            foreach (var relation in entity.ManyToOneRelationships
                     .Where(r =>
                            r.ReferencingEntity == entityname &&
                            r.ReferencingAttribute == linkitem.to &&
                            r.ReferencedEntity == linkitem.name &&
                            r.ReferencedAttribute == linkitem.from))
            {
                child = false;
                return(relation.ReferencingEntityNavigationPropertyName);
            }
            foreach (var relation in entity.ManyToManyRelationships
                     .Where(r =>
                            r.Entity1LogicalName == entityname &&
                            r.Entity1IntersectAttribute == linkitem.from))
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    if (relation.Entity2LogicalName == nextlink.name &&
                        relation.Entity2IntersectAttribute == nextlink.to)
                    {
                        child = true;
                        manyToManyNextLink = nextlink;
                        return(relation.Entity1NavigationPropertyName);
                    }
                }
            }
            foreach (var relation in entity.ManyToManyRelationships
                     .Where(r =>
                            r.Entity2LogicalName == entityname &&
                            r.Entity2IntersectAttribute == linkitem.from))
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    if (relation.Entity1LogicalName == nextlink.name &&
                        relation.Entity1IntersectAttribute == nextlink.from)
                    {
                        child = true;
                        manyToManyNextLink = nextlink;
                        return(relation.Entity2NavigationPropertyName);
                    }
                }
            }
            throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}");
        }
예제 #19
0
        public override IDataExecutionPlanNode FoldQuery(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints)
        {
            LeftSource         = LeftSource.FoldQuery(dataSources, options, parameterTypes, hints);
            LeftSource.Parent  = this;
            RightSource        = RightSource.FoldQuery(dataSources, options, parameterTypes, hints);
            RightSource.Parent = this;

            if (SemiJoin)
            {
                return(this);
            }

            var leftSchema  = LeftSource.GetSchema(dataSources, parameterTypes);
            var rightSchema = RightSource.GetSchema(dataSources, parameterTypes);

            if (LeftSource is FetchXmlScan leftFetch && RightSource is FetchXmlScan rightFetch)
            {
                // Can't join data from different sources
                if (!leftFetch.DataSource.Equals(rightFetch.DataSource, StringComparison.OrdinalIgnoreCase))
                {
                    return(this);
                }

                // If one source is distinct and the other isn't, joining the two won't produce the expected results
                if (leftFetch.FetchXml.distinct ^ rightFetch.FetchXml.distinct)
                {
                    return(this);
                }

                // Check that the alias is valid for FetchXML
                if (!FetchXmlScan.IsValidAlias(rightFetch.Alias))
                {
                    return(this);
                }

                var leftEntity  = leftFetch.Entity;
                var rightEntity = rightFetch.Entity;

                // Check that the join is on columns that are available in the FetchXML
                var leftAttribute = LeftAttribute.GetColumnName();
                if (!leftSchema.ContainsColumn(leftAttribute, out leftAttribute))
                {
                    return(this);
                }
                var rightAttribute = RightAttribute.GetColumnName();
                if (!rightSchema.ContainsColumn(rightAttribute, out rightAttribute))
                {
                    return(this);
                }
                var leftAttributeParts  = leftAttribute.Split('.');
                var rightAttributeParts = rightAttribute.Split('.');
                if (leftAttributeParts.Length != 2)
                {
                    return(this);
                }
                if (rightAttributeParts.Length != 2)
                {
                    return(this);
                }

                // If the entities are from different virtual entity data providers it's probably not going to work
                if (!dataSources.TryGetValue(leftFetch.DataSource, out var dataSource))
                {
                    throw new NotSupportedQueryFragmentException("Missing datasource " + leftFetch.DataSource);
                }

                if (dataSource.Metadata[leftFetch.Entity.name].DataProviderId != dataSource.Metadata[rightFetch.Entity.name].DataProviderId)
                {
                    return(this);
                }

                // Check we're not going to have too many link entities
                var leftLinkCount  = leftFetch.Entity.GetLinkEntities().Count();
                var rightLinkCount = rightFetch.Entity.GetLinkEntities().Count() + 1;

                if (leftLinkCount + rightLinkCount > 10)
                {
                    return(this);
                }

                // If we're doing a right outer join, switch everything round to do a left outer join
                // Also switch join order for inner joins to use N:1 relationships instead of 1:N to avoid problems with paging
                if (JoinType == QualifiedJoinType.RightOuter ||
                    JoinType == QualifiedJoinType.Inner && !rightAttributeParts[0].Equals(rightFetch.Alias, StringComparison.OrdinalIgnoreCase) ||
                    JoinType == QualifiedJoinType.Inner && leftAttribute == leftSchema.PrimaryKey && rightAttribute != rightSchema.PrimaryKey)
                {
                    Swap(ref leftFetch, ref rightFetch);
                    Swap(ref leftEntity, ref rightEntity);
                    Swap(ref leftAttribute, ref rightAttribute);
                    Swap(ref leftAttributeParts, ref rightAttributeParts);
                    Swap(ref leftSchema, ref rightSchema);
                }

                // Must be joining to the root entity of the right source, i.e. not a child link-entity
                if (!rightAttributeParts[0].Equals(rightFetch.Alias, StringComparison.OrdinalIgnoreCase))
                {
                    return(this);
                }

                // If there are any additional join criteria, either they must be able to be translated to FetchXml criteria
                // in the new link entity or we must be using an inner join so we can use a post-filter node
                var additionalCriteria     = AdditionalJoinCriteria;
                var additionalLinkEntities = new Dictionary <object, List <FetchLinkEntityType> >();

                if (TranslateFetchXMLCriteria(dataSource.Metadata, options, additionalCriteria, rightSchema, rightFetch.Alias, rightEntity.name, rightFetch.Alias, rightEntity.Items, out var filter, additionalLinkEntities))
                {
                    rightEntity.AddItem(filter);

                    foreach (var kvp in additionalLinkEntities)
                    {
                        if (kvp.Key is FetchEntityType e)
                        {
                            foreach (var le in kvp.Value)
                            {
                                rightEntity.AddItem(le);
                            }
                        }
                        else
                        {
                            foreach (var le in kvp.Value)
                            {
                                ((FetchLinkEntityType)kvp.Key).AddItem(le);
                            }
                        }
                    }

                    additionalCriteria = null;
                }

                if (additionalCriteria != null && JoinType != QualifiedJoinType.Inner)
                {
                    return(this);
                }

                var rightLinkEntity = new FetchLinkEntityType
                {
                    alias    = rightFetch.Alias,
                    name     = rightEntity.name,
                    linktype = JoinType == QualifiedJoinType.Inner ? "inner" : "outer",
                    from     = rightAttributeParts[1].ToLowerInvariant(),
                    to       = leftAttributeParts[1].ToLowerInvariant(),
                    Items    = rightEntity.Items
                };

                // Find where the two FetchXml documents should be merged together and return the merged version
                if (leftAttributeParts[0].Equals(leftFetch.Alias))
                {
                    if (leftEntity.Items == null)
                    {
                        leftEntity.Items = new object[] { rightLinkEntity }
                    }
                    ;
                    else
                    {
                        leftEntity.Items = leftEntity.Items.Concat(new object[] { rightLinkEntity }).ToArray();
                    }
                }
                else
                {
                    var leftLinkEntity = leftFetch.Entity.FindLinkEntity(leftAttributeParts[0]);

                    if (leftLinkEntity == null)
                    {
                        return(this);
                    }

                    if (leftLinkEntity.Items == null)
                    {
                        leftLinkEntity.Items = new object[] { rightLinkEntity }
                    }
                    ;
                    else
                    {
                        leftLinkEntity.Items = leftLinkEntity.Items.Concat(new object[] { rightLinkEntity }).ToArray();
                    }
                }

                if (additionalCriteria != null)
                {
                    return new FilterNode {
                               Filter = additionalCriteria, Source = leftFetch
                    }
                }
        private static string LinkItemToNavigationProperty(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender, out bool child)
        {
            GetEntityMetadata(entityname, sender);
            var entity = sender.entities[entityname];

            foreach (var relation in entity.OneToManyRelationships
                     .Where(r =>
                            r.ReferencedEntity == entityname &&
                            r.ReferencedAttribute == linkitem.to &&
                            r.ReferencingEntity == linkitem.name &&
                            r.ReferencingAttribute == linkitem.from))
            {
                if (linkitem.linktype != "outer")
                {
                    throw new ApplicationException($"OData queries do not support inner joins on 1:N relationships. Try changing link to {linkitem.name} to an outer join");
                }

                child = true;
                return(relation.ReferencedEntityNavigationPropertyName);
            }
            foreach (var relation in entity.ManyToOneRelationships
                     .Where(r =>
                            r.ReferencingEntity == entityname &&
                            r.ReferencingAttribute == linkitem.to &&
                            r.ReferencedEntity == linkitem.name &&
                            r.ReferencedAttribute == linkitem.from))
            {
                // OData $expand is equivalent to an outer join. Replicate inner join behaviour by adding a not-null filter on primary key
                // of related record type
                if (linkitem.linktype != "outer")
                {
                    if (linkitem.Items == null)
                    {
                        linkitem.Items = new object[0];
                    }
                    var filter = linkitem.Items.OfType <filter>().FirstOrDefault(f => f.type == filterType.and);
                    if (filter == null)
                    {
                        filter = new filter
                        {
                            type  = filterType.and,
                            Items = new object[0]
                        };
                        var items = new List <object>(linkitem.Items);
                        items.Add(filter);
                        linkitem.Items = items.ToArray();
                    }
                    GetEntityMetadata(linkitem.name, sender);
                    var linkedEntity = sender.entities[linkitem.name];
                    var condition    = filter.Items.OfType <condition>()
                                       .FirstOrDefault(c => c.attribute == linkedEntity.PrimaryIdAttribute && c.@operator == @operator.notnull);

                    if (condition == null)
                    {
                        condition = new condition
                        {
                            attribute = linkedEntity.PrimaryIdAttribute,
                            @operator = @operator.notnull
                        };
                        var items = new List <object>(filter.Items);
                        items.Add(condition);
                        filter.Items = items.ToArray();
                    }
                }

                child = false;
                return(relation.ReferencingEntityNavigationPropertyName);
            }
            foreach (var relation in entity.ManyToManyRelationships
                     .Where(r =>
                            r.Entity1LogicalName == entityname &&
                            r.Entity1IntersectAttribute == linkitem.from))
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    if (nextlink.linktype != "outer")
                    {
                        throw new Exception($"OData queries do not support inner joins on N:N relationships. Try changing link to {nextlink.name} to an outer join");
                    }
                    if (relation.Entity2LogicalName == nextlink.name &&
                        relation.Entity2IntersectAttribute == nextlink.to)
                    {
                        child = true;
                        return(relation.Entity1NavigationPropertyName);
                    }
                }
            }
            foreach (var relation in entity.ManyToManyRelationships
                     .Where(r =>
                            r.Entity2LogicalName == entityname &&
                            r.Entity2IntersectAttribute == linkitem.from))
            {
                var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
                if (linkitems.Count > 1)
                {
                    throw new Exception("Invalid M:M-relation definition for OData");
                }
                if (linkitems.Count == 1)
                {
                    var nextlink = (FetchLinkEntityType)linkitems[0];
                    if (nextlink.linktype != "outer")
                    {
                        throw new Exception($"OData queries do not support inner joins on N:N relationships. Try changing link to {nextlink.name} to an outer join");
                    }
                    if (relation.Entity1LogicalName == nextlink.name &&
                        relation.Entity1IntersectAttribute == nextlink.from)
                    {
                        child = true;
                        return(relation.Entity2NavigationPropertyName);
                    }
                }
            }
            throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}");
        }
        private void WriteLinkEntity(string parentEntityName, FetchLinkEntityType link)
        {
            bool isFirstLineWrited = false;

            WriteLine($"new {nameof(LinkEntity)}()");
            WriteLine("{");

            if (string.Equals(link.linktype, "outer", StringComparison.InvariantCulture))
            {
                WriteLineIfHasLine(ref isFirstLineWrited);
                WriteLine($"{nameof(LinkEntity.JoinOperator)} = {nameof(JoinOperator)}.{nameof(JoinOperator.LeftOuter)},");
            }

            WriteLineIfHasLine(ref isFirstLineWrited);

            WriteLine($"{nameof(LinkEntity.LinkFromEntityName)} = \"{parentEntityName}\",");
            WriteLine($"{nameof(LinkEntity.LinkFromAttributeName)} = \"{link.to}\",");

            WriteLine();

            WriteLine($"{nameof(LinkEntity.LinkToEntityName)} = \"{link.name}\",");
            WriteLine($"{nameof(LinkEntity.LinkToAttributeName)} = \"{link.from}\",");

            if (!string.IsNullOrEmpty(link.alias) && !string.IsNullOrWhiteSpace(link.alias))
            {
                WriteLine();
                WriteLine($"{nameof(LinkEntity.EntityAlias)} = \"{link.alias}\",");
            }

            if (IsColumnSetNotDefault(link.Items))
            {
                var columnDefinition = GetColumnSetDefinition(link.Items);

                WriteLine();
                WriteLine($"{nameof(LinkEntity.Columns)} = {columnDefinition},");
            }

            var criteria = link.Items.OfType <filter>().Where(IsFilterExpressionNotEmpty).ToList();

            if (criteria.Any())
            {
                filter filter;

                if (criteria.Count == 1)
                {
                    filter = criteria[0];
                }
                else
                {
                    filter = new filter()
                    {
                        Items = criteria.ToArray(),
                    };
                }

                WriteLine();

                WriteLine($"{nameof(LinkEntity.LinkCriteria)} =");

                WriteLine("{");

                WriteFilterExpressionContent(filter);

                WriteLine("},");
            }

            if (link.Items.OfType <FetchLinkEntityType>().Any())
            {
                WriteLine();

                WriteLine($"{nameof(LinkEntity.LinkEntities)} =");

                WriteLine("{");

                bool isFirstLinkWrited = false;

                foreach (var sublink in link.Items.OfType <FetchLinkEntityType>())
                {
                    WriteLineIfHasLine(ref isFirstLinkWrited);
                    WriteLinkEntity(link.name, sublink);
                }

                WriteLine("},");
            }

            WriteLine("},");
        }
예제 #22
0
        public static FetchAttributeType FindAliasedAttribute(this FetchEntityType entity, string colName, Func <FetchAttributeType, bool> predicate, out FetchLinkEntityType linkEntity)
        {
            linkEntity = null;

            return(FindAliasedAttribute(entity.Items, colName, predicate, ref linkEntity));
        }
예제 #23
0
        public override IDataExecutionPlanNode FoldQuery(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IList <OptimizerHint> hints)
        {
            Source        = Source.FoldQuery(dataSources, options, parameterTypes, hints);
            Source.Parent = this;

            // Foldable correlated IN queries "lefttable.column IN (SELECT righttable.column FROM righttable WHERE ...) are created as:
            // Filter: Expr2 is not null
            // -> FoldableJoin (LeftOuter SemiJoin) Expr2 = righttable.column in DefinedValues; righttable.column in RightAttribute
            //    -> FetchXml
            //    -> FetchXml (Distinct) orderby righttable.column

            // Foldable correlated EXISTS filters "EXISTS (SELECT * FROM righttable WHERE righttable.column = lefttable.column AND ...) are created as:
            // Filter - @var2 is not null
            // -> NestedLoop(LeftOuter SemiJoin), null join condition. Outer reference(lefttable.column -> @var1), Defined values(@var2 -> rightttable.primarykey)
            //   -> FetchXml
            //   -> Top 1
            //      -> Index spool, SeekValue @var1, KeyColumn rightttable.column
            //         -> FetchXml
            var joins = new List <BaseJoinNode>();
            var join  = Source as BaseJoinNode;

            while (join != null)
            {
                joins.Add(join);

                if (join is MergeJoinNode && join.LeftSource is SortNode sort)
                {
                    join = sort.Source as BaseJoinNode;
                }
                else
                {
                    join = join.LeftSource as BaseJoinNode;
                }
            }

            var          addedLinks = new List <FetchLinkEntityType>();
            FetchXmlScan leftFetch;

            if (joins.Count == 0)
            {
                leftFetch = null;
            }
            else
            {
                var lastJoin = joins.Last();
                if (lastJoin is MergeJoinNode && lastJoin.LeftSource is SortNode sort)
                {
                    leftFetch = sort.Source as FetchXmlScan;
                }
                else
                {
                    leftFetch = lastJoin.LeftSource as FetchXmlScan;
                }
            }

            while (leftFetch != null && joins.Count > 0)
            {
                join = joins.Last();

                if (join.JoinType != QualifiedJoinType.LeftOuter ||
                    !join.SemiJoin)
                {
                    break;
                }

                FetchLinkEntityType linkToAdd;
                string leftAlias;

                if (join is FoldableJoinNode merge)
                {
                    // Check we meet all the criteria for a foldable correlated IN query
                    var rightSort  = join.RightSource as SortNode;
                    var rightFetch = (rightSort?.Source ?? join.RightSource) as FetchXmlScan;

                    if (rightFetch == null)
                    {
                        break;
                    }

                    if (!leftFetch.DataSource.Equals(rightFetch.DataSource, StringComparison.OrdinalIgnoreCase))
                    {
                        break;
                    }

                    // Sorts could be folded into FetchXML or could be in separate node
                    string attribute;

                    if (rightSort != null)
                    {
                        if (rightSort.Sorts.Count != 1)
                        {
                            break;
                        }

                        if (!(rightSort.Sorts[0].Expression is ColumnReferenceExpression sortCol))
                        {
                            break;
                        }

                        attribute = sortCol.GetColumnName();
                    }
                    else
                    {
                        var rightSorts = (rightFetch.Entity.Items ?? Array.Empty <object>()).OfType <FetchOrderType>().ToList();

                        if (rightSorts.Count != 1)
                        {
                            break;
                        }

                        if (!String.IsNullOrEmpty(rightSorts[0].alias))
                        {
                            break;
                        }

                        attribute = $"{rightFetch.Alias}.{rightSorts[0].attribute}";
                    }

                    if (!merge.RightAttribute.GetColumnName().Equals(attribute, StringComparison.OrdinalIgnoreCase))
                    {
                        break;
                    }

                    var rightSchema = rightFetch.GetSchema(dataSources, parameterTypes);

                    // Right values need to be distinct - still allowed if it's the primary key
                    if (!rightFetch.FetchXml.distinct && rightSchema.PrimaryKey != attribute)
                    {
                        break;
                    }

                    var definedValueName = join.DefinedValues.SingleOrDefault(kvp => kvp.Value == attribute).Key;

                    if (definedValueName == null)
                    {
                        break;
                    }

                    var notNullFilter = FindNotNullFilter(Filter, definedValueName);
                    if (notNullFilter == null)
                    {
                        break;
                    }

                    // We can fold IN to a simple left outer join where the attribute is the primary key
                    if (!rightFetch.FetchXml.distinct && rightSchema.PrimaryKey == attribute)
                    {
                        // Replace the filter on the defined value name with a filter on the primary key column
                        notNullFilter.Expression = attribute.ToColumnReference();

                        linkToAdd = new FetchLinkEntityType
                        {
                            name     = rightFetch.Entity.name,
                            alias    = rightFetch.Alias,
                            from     = merge.RightAttribute.MultiPartIdentifier.Identifiers.Last().Value,
                            to       = merge.LeftAttribute.MultiPartIdentifier.Identifiers.Last().Value,
                            linktype = "outer",
                            Items    = rightFetch.Entity.Items.Where(i => !(i is FetchOrderType)).ToArray()
                        };
                    }
                    else
                    {
                        // We need to use an "in" join type - check that's supported
                        if (!options.JoinOperatorsAvailable.Contains(JoinOperator.Any))
                        {
                            break;
                        }

                        // Remove the filter and replace with an "in" link-entity
                        Filter = Filter.RemoveCondition(notNullFilter);

                        linkToAdd = new FetchLinkEntityType
                        {
                            name     = rightFetch.Entity.name,
                            alias    = rightFetch.Alias,
                            from     = merge.RightAttribute.MultiPartIdentifier.Identifiers.Last().Value,
                            to       = merge.LeftAttribute.MultiPartIdentifier.Identifiers.Last().Value,
                            linktype = "in",
                            Items    = rightFetch.Entity.Items.Where(i => !(i is FetchOrderType)).ToArray()
                        };
                    }

                    leftAlias = merge.LeftAttribute.MultiPartIdentifier.Identifiers.Reverse().Skip(1).First().Value;

                    // Remove the sort that has been merged into the left side too
                    if (leftFetch.Entity.Items != null)
                    {
                        leftFetch.Entity.Items = leftFetch.Entity
                                                 .Items
                                                 .Where(i => !(i is FetchOrderType sort) || !sort.attribute.Equals(merge.LeftAttribute.MultiPartIdentifier.Identifiers.Last().Value, StringComparison.OrdinalIgnoreCase))
                                                 .ToArray();
                    }
                }
                else if (join is NestedLoopNode loop)
                {
                    // Check we meet all the criteria for a foldable correlated IN query
                    if (!options.JoinOperatorsAvailable.Contains(JoinOperator.Exists))
                    {
                        break;
                    }

                    if (loop.JoinCondition != null ||
                        loop.OuterReferences.Count != 1 ||
                        loop.DefinedValues.Count != 1)
                    {
                        break;
                    }

                    if (!(join.RightSource is TopNode top))
                    {
                        break;
                    }

                    if (!(top.Top is IntegerLiteral topLiteral) ||
                        topLiteral.Value != "1")
                    {
                        break;
                    }

                    if (!(top.Source is IndexSpoolNode indexSpool))
                    {
                        break;
                    }

                    if (indexSpool.SeekValue != loop.OuterReferences.Single().Value)
                    {
                        break;
                    }

                    if (!(indexSpool.Source is FetchXmlScan rightFetch))
                    {
                        break;
                    }

                    if (indexSpool.KeyColumn.Split('.').Length != 2 ||
                        !indexSpool.KeyColumn.Split('.')[0].Equals(rightFetch.Alias, StringComparison.OrdinalIgnoreCase))
                    {
                        break;
                    }

                    var notNullFilter = FindNotNullFilter(Filter, loop.DefinedValues.Single().Key);
                    if (notNullFilter == null)
                    {
                        break;
                    }

                    // Remove the filter and replace with an "exists" link-entity
                    Filter = Filter.RemoveCondition(notNullFilter);

                    linkToAdd = new FetchLinkEntityType
                    {
                        name     = rightFetch.Entity.name,
                        alias    = rightFetch.Alias,
                        from     = indexSpool.KeyColumn.Split('.')[1],
                        to       = loop.OuterReferences.Single().Key.Split('.')[1],
                        linktype = "exists",
                        Items    = rightFetch.Entity.Items
                    };
                    leftAlias = loop.OuterReferences.Single().Key.Split('.')[0];
                }
                else
                {
                    // This isn't a type of join we can fold as a correlated IN/EXISTS join
                    break;
                }

                // Remove any attributes from the new linkentity
                var tempEntity = new FetchEntityType {
                    Items = new object[] { linkToAdd }
                };

                foreach (var link in tempEntity.GetLinkEntities())
                {
                    link.Items = (link.Items ?? Array.Empty <object>()).Where(i => !(i is FetchAttributeType) && !(i is allattributes)).ToArray();
                }

                if (leftAlias.Equals(leftFetch.Alias, StringComparison.OrdinalIgnoreCase))
                {
                    leftFetch.Entity.AddItem(linkToAdd);
                }
                else
                {
                    leftFetch.Entity.FindLinkEntity(leftAlias).AddItem(linkToAdd);
                }

                addedLinks.Add(linkToAdd);

                joins.Remove(join);

                if (joins.Count == 0)
                {
                    Source           = leftFetch;
                    leftFetch.Parent = this;
                }
                else
                {
                    join = joins.Last();

                    if (join is MergeJoinNode && join.LeftSource is SortNode sort)
                    {
                        sort.Source      = leftFetch;
                        leftFetch.Parent = sort;
                    }
                    else
                    {
                        join.LeftSource  = leftFetch;
                        leftFetch.Parent = join;
                    }
                }
            }

            // If we've got a filter matching a column and a variable (key lookup in a nested loop) from a table spool, replace it with a index spool
            if (Source is TableSpoolNode tableSpool)
            {
                var schema = Source.GetSchema(dataSources, parameterTypes);

                if (ExtractKeyLookupFilter(Filter, out var filter, out var indexColumn, out var seekVariable) && schema.ContainsColumn(indexColumn, out indexColumn))
                {
                    var spoolSource = tableSpool.Source;

                    // Index spool requires non-null key values
                    if (indexColumn != schema.PrimaryKey)
                    {
                        spoolSource = new FilterNode
                        {
                            Source = tableSpool.Source,
                            Filter = new BooleanIsNullExpression
                            {
                                Expression = indexColumn.ToColumnReference(),
                                IsNot      = true
                            }
                        }.FoldQuery(dataSources, options, parameterTypes, hints);
                    }

                    Source = new IndexSpoolNode
                    {
                        Source    = spoolSource,
                        KeyColumn = indexColumn,
                        SeekValue = seekVariable
                    };

                    Filter = filter;
                }
            }

            // Find all the data source nodes we could fold this into. Include direct data sources, those from either side of an inner join, or the main side of an outer join
            foreach (var source in GetFoldableSources(Source))
            {
                var schema = source.GetSchema(dataSources, parameterTypes);

                if (source is FetchXmlScan fetchXml && !fetchXml.FetchXml.aggregate)
                {
                    if (!dataSources.TryGetValue(fetchXml.DataSource, out var dataSource))
                    {
                        throw new NotSupportedQueryFragmentException("Missing datasource " + fetchXml.DataSource);
                    }

                    var additionalLinkEntities = new Dictionary <object, List <FetchLinkEntityType> >();

                    // If the criteria are ANDed, see if any of the individual conditions can be translated to FetchXML
                    Filter = ExtractFetchXMLFilters(dataSource.Metadata, options, Filter, schema, null, fetchXml.Entity.name, fetchXml.Alias, fetchXml.Entity.Items, out var fetchFilter, additionalLinkEntities);

                    if (fetchFilter != null)
                    {
                        fetchXml.Entity.AddItem(fetchFilter);

                        foreach (var kvp in additionalLinkEntities)
                        {
                            if (kvp.Key is FetchEntityType e)
                            {
                                foreach (var le in kvp.Value)
                                {
                                    fetchXml.Entity.AddItem(le);
                                }
                            }
                            else
                            {
                                foreach (var le in kvp.Value)
                                {
                                    ((FetchLinkEntityType)kvp.Key).AddItem(le);
                                }
                            }
                        }
                    }
                }

                if (source is MetadataQueryNode meta)
                {
                    // If the criteria are ANDed, see if any of the individual conditions can be translated to the metadata query
                    Filter = ExtractMetadataFilters(Filter, meta, options, out var entityFilter, out var attributeFilter, out var relationshipFilter);

                    meta.Query.AddFilter(entityFilter);

                    if (attributeFilter != null && meta.Query.AttributeQuery == null)
                    {
                        meta.Query.AttributeQuery = new AttributeQueryExpression();
                    }

                    meta.Query.AttributeQuery.AddFilter(attributeFilter);

                    if (relationshipFilter != null && meta.Query.RelationshipQuery == null)
                    {
                        meta.Query.RelationshipQuery = new RelationshipQueryExpression();
                    }

                    meta.Query.RelationshipQuery.AddFilter(relationshipFilter);
                }
            }

            foreach (var addedLink in addedLinks)
            {
                addedLink.SemiJoin = true;
            }

            if (Filter == null)
            {
                return(Source);
            }

            return(this);
        }
 private static string GetExpandedSelect(FetchLinkEntityType linkitem, string relation, FetchXmlBuilder sender)
 {
     if (linkitem.Items == null)
     {
         return "";
     }
     var result = "";
     var linkentity = linkitem.name;
     var attributeitems = linkitem.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList();
     if (linkitem.intersect)
     {
         var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
         if (linkitems.Count > 1)
         {
             throw new Exception("Invalid M:M-relation definition for OData");
         }
         if (linkitems.Count == 1)
         {
             var nextlink = (FetchLinkEntityType)linkitems[0];
             linkentity = nextlink.name;
             attributeitems = nextlink.Items.Where(i => i is FetchAttributeType && ((FetchAttributeType)i).name != null).ToList();
         }
     }
     if (attributeitems.Count > 0)
     {
         foreach (FetchAttributeType attributeitem in attributeitems)
         {
             result += relation + "/" + LogicalToSchemaName(linkentity, attributeitem.name, sender) + ",";
         }
     }
     return result;
 }
 private static RelationshipMetadataBase LinkItemToRelation(string entityname, FetchLinkEntityType linkitem, FetchXmlBuilder sender)
 {
     GetEntityMetadata(entityname, sender);
     var entity = FetchXmlBuilder.entities[entityname];
     foreach (var relation in entity.OneToManyRelationships)
     {
         if (relation.ReferencedEntity == entityname &&
             relation.ReferencedAttribute == linkitem.to &&
             relation.ReferencingEntity == linkitem.name &&
             relation.ReferencingAttribute == linkitem.from)
         {
             return relation;
         }
     }
     foreach (var relation in entity.ManyToOneRelationships)
     {
         if (relation.ReferencingEntity == entityname &&
             relation.ReferencingAttribute == linkitem.to &&
             relation.ReferencedEntity == linkitem.name &&
             relation.ReferencedAttribute == linkitem.from)
         {
             return relation;
         }
     }
     foreach (var relation in entity.ManyToManyRelationships)
     {
         if (relation.Entity1LogicalName == entityname &&
             relation.Entity1IntersectAttribute == linkitem.from)
         {
             var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
             if (linkitems.Count > 1)
             {
                 throw new Exception("Invalid M:M-relation definition for OData");
             }
             if (linkitems.Count == 1)
             {
                 var nextlink = (FetchLinkEntityType)linkitems[0];
                 if (nextlink.linktype == "outer")
                 {
                     throw new Exception("OData queries do not support outer joins");
                 }
                 if (relation.Entity2LogicalName == nextlink.name &&
                     relation.Entity2IntersectAttribute == nextlink.to)
                 {
                     return relation;
                 }
             }
         }
         if (relation.Entity2LogicalName == entityname &&
             relation.Entity2IntersectAttribute == linkitem.from)
         {
             var linkitems = linkitem.Items.Where(i => i is FetchLinkEntityType).ToList();
             if (linkitems.Count > 1)
             {
                 throw new Exception("Invalid M:M-relation definition for OData");
             }
             if (linkitems.Count == 1)
             {
                 var nextlink = (FetchLinkEntityType)linkitems[0];
                 if (nextlink.linktype == "outer")
                 {
                     throw new Exception("OData queries do not support outer joins");
                 }
                 if (relation.Entity1LogicalName == nextlink.name &&
                     relation.Entity1IntersectAttribute == nextlink.to)
                 {
                     return relation;
                 }
             }
         }
     }
     throw new Exception($"Cannot find metadata for relation {entityname}.{linkitem.to} => {linkitem.name}.{linkitem.from}");
 }