/// <summary> /// Extension method to join the attributes of entity e and otherEntity /// </summary> /// <param name="e"></param> /// <param name="otherEntity"></param> /// <param name="attributes"></param> /// <returns></returns> public static Entity JoinAttributes(this Entity e, Entity otherEntity, ColumnSet columnSet, string alias, XrmFakedContext context) { if (otherEntity == null) { return(e); //Left Join where otherEntity was not matched } otherEntity = otherEntity.Clone(); //To avoid joining entities from/to the same entities, which would cause collection modified exceptions if (columnSet.AllColumns) { foreach (var attKey in otherEntity.Attributes.Keys) { e[alias + "." + attKey] = new AliasedValue(otherEntity.LogicalName, attKey, otherEntity[attKey]); } foreach (var attKey in otherEntity.FormattedValues.Keys) { e.FormattedValues[alias + "." + attKey] = otherEntity.FormattedValues[attKey]; } } else { //Return selected list of attributes foreach (var attKey in columnSet.Columns) { if (!context.AttributeExistsInMetadata(otherEntity.LogicalName, attKey)) { OrganizationServiceFaultQueryBuilderNoAttributeException.Throw(attKey); } if (otherEntity.Attributes.ContainsKey(attKey)) { e[alias + "." + attKey] = new AliasedValue(otherEntity.LogicalName, attKey, otherEntity[attKey]); } else { e[alias + "." + attKey] = new AliasedValue(otherEntity.LogicalName, attKey, null); } if (otherEntity.FormattedValues.ContainsKey(attKey)) { e.FormattedValues[alias + "." + attKey] = otherEntity.FormattedValues[attKey]; } } } return(e); }
public static Entity ProjectAttributes(this Entity e, QueryExpression qe, XrmFakedContext context) { if (qe.ColumnSet == null) { return(e); } if (qe.ColumnSet.AllColumns) { return(e); //return all the original attributes } else { //Return selected list of attributes in a projected entity Entity projected = null; //However, if we are using proxy types, we must create a instance of the appropiate class if (context.ProxyTypesAssembly != null) { var subClassType = context.FindReflectedType(e.LogicalName); if (subClassType != null) { var instance = Activator.CreateInstance(subClassType); projected = (Entity)instance; projected.Id = e.Id; } else { projected = new Entity(e.LogicalName) { Id = e.Id } }; //fallback to generic type if type not found } else { projected = new Entity(e.LogicalName) { Id = e.Id } }; foreach (var attKey in qe.ColumnSet.Columns) { //Check if attribute really exists in metadata if (!context.AttributeExistsInMetadata(e.LogicalName, attKey)) { OrganizationServiceFaultQueryBuilderNoAttributeException.Throw(attKey); } if (e.Attributes.ContainsKey(attKey) && e.Attributes[attKey] != null) { projected[attKey] = CloneAttribute(e[attKey]); } } //Plus attributes from joins foreach (var le in qe.LinkEntities) { ProjectAttributes(e, projected, le, context); } //foreach (var attKey in e.Attributes.Keys) //{ // if(e[attKey] is AliasedValue && !projected.Attributes.ContainsKey(attKey)) // projected[attKey] = e[attKey]; //} return(projected); } }
public static IQueryable <Entity> TranslateLinkedEntityToLinq(XrmFakedContext context, LinkEntity le, IQueryable <Entity> query, ColumnSet previousColumnSet, string linkFromAlias = "") { var leAlias = string.IsNullOrWhiteSpace(le.EntityAlias) ? le.LinkToEntityName : le.EntityAlias; context.EnsureEntityNameExistsInMetadata(le.LinkFromEntityName); context.EnsureEntityNameExistsInMetadata(le.LinkToEntityName); if (!context.AttributeExistsInMetadata(le.LinkToEntityName, le.LinkToAttributeName)) { OrganizationServiceFaultQueryBuilderNoAttributeException.Throw(le.LinkToAttributeName); } var inner = context.CreateQuery <Entity>(le.LinkToEntityName); //if (!le.Columns.AllColumns && le.Columns.Columns.Count == 0) //{ // le.Columns.AllColumns = true; //Add all columns in the joined entity, otherwise we can't filter by related attributes, then the Select will actually choose which ones we need //} if (string.IsNullOrWhiteSpace(linkFromAlias)) { linkFromAlias = le.LinkFromAttributeName; } else { linkFromAlias += "." + le.LinkFromAttributeName; } switch (le.JoinOperator) { case JoinOperator.Inner: case JoinOperator.Natural: query = query.Join(inner, outerKey => outerKey.KeySelector(linkFromAlias, context), innerKey => innerKey.KeySelector(le.LinkToAttributeName, context), (outerEl, innerEl) => outerEl .JoinAttributes(innerEl, new ColumnSet(true), leAlias, context)); break; case JoinOperator.LeftOuter: query = query.GroupJoin(inner, outerKey => outerKey.KeySelector(le.LinkFromAttributeName, context), innerKey => innerKey.KeySelector(le.LinkToAttributeName, context), (outerEl, innerElemsCol) => new { outerEl, innerElemsCol }) .SelectMany(x => x.innerElemsCol.DefaultIfEmpty() , (x, y) => x.outerEl .JoinAttributes(y, new ColumnSet(true), leAlias, context)); break; default: //This shouldn't be reached as there are only 3 types of Join... throw new PullRequestException(string.Format("The join operator {0} is currently not supported. Feel free to implement and send a PR.", le.JoinOperator)); } //Process nested linked entities recursively foreach (LinkEntity nestedLinkedEntity in le.LinkEntities) { if (string.IsNullOrWhiteSpace(le.EntityAlias)) { le.EntityAlias = le.LinkToEntityName; } query = TranslateLinkedEntityToLinq(context, nestedLinkedEntity, query, le.Columns, le.EntityAlias); } return(query); }