/// <summary> /// Fixes the range of the restriction in accordance with <paramref name="range"/>. /// Member restriction must be complete for this operation. /// </summary> internal override DomainBoolExpr FixRange(Set<Constant> range, MemberDomainMap memberDomainMap) { Debug.Assert(IsComplete, "Ranges are fixed only for complete scalar restrictions."); IEnumerable<Constant> newPossibleValues = memberDomainMap.GetDomain(RestrictedMemberSlot.MemberPath); BoolLiteral newLiteral = new ScalarRestriction(RestrictedMemberSlot, new Domain(range, newPossibleValues)); return newLiteral.GetDomainBoolExpression(memberDomainMap); }
internal QueryRewriter(EdmType generatedType, ViewgenContext context, ViewGenMode typesGenerationMode) { Debug.Assert(typesGenerationMode != ViewGenMode.GenerateAllViews); _typesGenerationMode = typesGenerationMode; _context = context; _generatedType = generatedType; _domainMap = context.MemberMaps.LeftDomainMap; _config = context.Config; _identifiers = context.CqlIdentifiers; _qp = new RewritingProcessor<Tile<FragmentQuery>>(new DefaultTileProcessor<FragmentQuery>(context.LeftFragmentQP)); _extentPath = new MemberPath(context.Extent); _keyAttributes = new List<MemberPath>(MemberPath.GetKeyMembers(context.Extent, _domainMap)); // populate _fragmentQueries and _views foreach (var leftCellWrapper in _context.AllWrappersForExtent) { var query = leftCellWrapper.FragmentQuery; Tile<FragmentQuery> tile = CreateTile(query); _fragmentQueries.Add(query); _views.Add(tile); } Debug.Assert(_views.Count > 0); AdjustMemberDomainsForUpdateViews(); // must be done after adjusting domains _domainQuery = GetDomainQuery(FragmentQueries, generatedType); _usedViews = new HashSet<FragmentQuery>(); }
internal override BoolExpr<DomainConstraint<BoolLiteral, Constant>> GetDomainBoolExpression(MemberDomainMap domainMap) { // Essentially say that the variable can take values true or false and here its value is only true IEnumerable<Constant> actualValues = new Constant[] { new ScalarConstant(true) }; IEnumerable<Constant> possibleValues = new Constant[] { new ScalarConstant(true), new ScalarConstant(false) }; var variableDomain = new Set<Constant>(possibleValues, Constant.EqualityComparer).MakeReadOnly(); var thisDomain = new Set<Constant>(actualValues, Constant.EqualityComparer).MakeReadOnly(); var result = MakeTermExpression(this, variableDomain, thisDomain); return result; }
// effects: Creates a view generator object that can be used to generate views // based on usedCells (projectedSlotMap are useful for deciphering the fields) internal BasicViewGenerator(MemberProjectionIndex projectedSlotMap, List<LeftCellWrapper> usedCells, FragmentQuery activeDomain, ViewgenContext context, MemberDomainMap domainMap, ErrorLog errorLog, ConfigViewGenerator config) { Debug.Assert(usedCells.Count > 0, "No used cells"); m_projectedSlotMap = projectedSlotMap; m_usedCells = usedCells; m_viewgenContext = context; m_activeDomain = activeDomain; m_errorLog = errorLog; m_config = config; m_domainMap = domainMap; }
internal MemberMaps(ViewTarget viewTarget, MemberProjectionIndex projectedSlotMap, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap) { m_projectedSlotMap = projectedSlotMap; m_queryDomainMap = queryDomainMap; m_updateDomainMap = updateDomainMap; Debug.Assert(m_queryDomainMap != null); Debug.Assert(m_updateDomainMap != null); Debug.Assert(m_projectedSlotMap != null); m_viewTarget = viewTarget; }
internal override BoolExpr<DomainConstraint<BoolLiteral, Constant>> FixRange(Set<Constant> range, MemberDomainMap memberDomainMap) { Debug.Assert(range.Count == 1, "For BoolLiterals, there should be precisely one value - true or false"); var scalar = (ScalarConstant)range.First(); var expr = GetDomainBoolExpression(memberDomainMap); if ((bool)scalar.Value == false) { // The range of the variable was "inverted". Return a NOT of // the expression expr = new NotExpr<DomainConstraint<BoolLiteral, Constant>>(expr); } return expr; }
// effects: Creates a ViewGenerator object that is capable of // producing query or update mapping views given the relevant schema // given the "cells" internal ViewGenerator( CellGroup cellGroup, ConfigViewGenerator config, List<ForeignConstraint> foreignKeyConstraints, StorageEntityContainerMapping entityContainerMapping) { m_cellGroup = cellGroup; m_config = config; m_queryRewriterCache = new Dictionary<EntitySetBase, QueryRewriter>(); m_foreignKeyConstraints = foreignKeyConstraints; m_entityContainerMapping = entityContainerMapping; var inheritanceGraph = MetadataHelper.BuildUndirectedGraphOfTypes(entityContainerMapping.StorageMappingItemCollection.EdmItemCollection); SetConfiguration(entityContainerMapping); // We fix all the cells at this point m_queryDomainMap = new MemberDomainMap( ViewTarget.QueryView, m_config.IsValidationEnabled, cellGroup, entityContainerMapping.StorageMappingItemCollection.EdmItemCollection, m_config, inheritanceGraph); m_updateDomainMap = new MemberDomainMap( ViewTarget.UpdateView, m_config.IsValidationEnabled, cellGroup, entityContainerMapping.StorageMappingItemCollection.EdmItemCollection, m_config, inheritanceGraph); // We now go and fix the queryDomain map so that it has all the // values from the S-side as well -- this is needed for domain // constraint propagation, i.e., values from the S-side get // propagated to te oneOfConst on the C-side. So we better get // the "possiblveValues" stuff to contain those constants as well MemberDomainMap.PropagateUpdateDomainToQueryDomain(cellGroup, m_queryDomainMap, m_updateDomainMap); UpdateWhereClauseForEachCell(cellGroup, m_queryDomainMap, m_updateDomainMap, m_config); // We need to simplify cell queries, yet we don't want the conditions to disappear // So, add an extra value to the domain, temporarily var queryOpenDomain = m_queryDomainMap.GetOpenDomain(); var updateOpenDomain = m_updateDomainMap.GetOpenDomain(); // Make sure the WHERE clauses of the cells reflect the changes foreach (var cell in cellGroup) { cell.CQuery.WhereClause.FixDomainMap(queryOpenDomain); cell.SQuery.WhereClause.FixDomainMap(updateOpenDomain); cell.CQuery.WhereClause.ExpensiveSimplify(); cell.SQuery.WhereClause.ExpensiveSimplify(); cell.CQuery.WhereClause.FixDomainMap(m_queryDomainMap); cell.SQuery.WhereClause.FixDomainMap(m_updateDomainMap); } }
// effects: Given the extent cells and a map for the domains of all // variables in it, fixes the cell constant domains of the where // clauses in the left queries of cells (left is defined using viewTarget) private static void UpdateWhereClauseForEachCell( IEnumerable<Cell> extentCells, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap, ConfigViewGenerator config) { foreach (var cell in extentCells) { cell.CQuery.UpdateWhereClause(queryDomainMap); cell.SQuery.UpdateWhereClause(updateDomainMap); } // Fix enumerable domains - currently it is only applicable to boolean type. Note that it is // not applicable to enumerated types since we allow any value of the underlying type of the enum type. queryDomainMap.ReduceEnumerableDomainToEnumeratedValues(config); updateDomainMap.ReduceEnumerableDomainToEnumeratedValues(config); }
// effects: Create a boolean expression from a literal value internal static BoolExpression CreateLiteral(BoolLiteral literal, MemberDomainMap memberDomainMap) { DomainBoolExpr expr = literal.GetDomainBoolExpression(memberDomainMap); return(new BoolExpression(expr, memberDomainMap)); }
/// <summary> /// Returns a boolean expression that is domain-aware and ready for optimizations etc. /// </summary> /// <param name="domainMap">Maps members to the values that each member can take; /// it can be null in which case the possible and actual values are the same.</param> internal override DomainBoolExpr GetDomainBoolExpression(MemberDomainMap domainMap) { // Get the variable name from the slot's memberpath and the possible domain values from the slot DomainTermExpr result; if (domainMap != null) { // Look up the domain from the domainMap IEnumerable<Constant> domain = domainMap.GetDomain(m_restrictedMemberSlot.MemberPath); result = MakeTermExpression(this, domain, m_domain.Values); } else { result = MakeTermExpression(this, m_domain.AllPossibleValues, m_domain.Values); } return result; }
// effects: Merges query2 with this according to the TM/SP rules for opType and // returns the merged result. canBooleansOverlap indicates whether the bools in this and query2 can overlap, i.e. // the same cells may have contributed to query2 and this earlier in the merge process internal bool TryMergeTwoCellQueries(CellQuery query1, CellQuery query2, CellTreeOpType opType, MemberDomainMap memberDomainMap, out CellQuery mergedQuery) { mergedQuery = null; // Initialize g1 and g2 according to the TM/SP rules for IJ, LOJ, Union, FOJ cases BoolExpression g1 = null; BoolExpression g2 = null; switch (opType) { case CellTreeOpType.IJ: break; case CellTreeOpType.LOJ: case CellTreeOpType.LASJ: g2 = BoolExpression.True; break; case CellTreeOpType.FOJ: case CellTreeOpType.Union: g1 = BoolExpression.True; g2 = BoolExpression.True; break; default: Debug.Fail("Unsupported operator"); break; } Dictionary<MemberPath, MemberPath> remap = new Dictionary<MemberPath, MemberPath>(MemberPath.EqualityComparer); //Continue merging only if both queries are over the same source MemberPath newRoot; if (!query1.Extent.Equals(query2.Extent)) { // could not merge return false; } else { newRoot = query1.SourceExtentMemberPath; } // Conjuncts for ANDing with the previous whereClauses BoolExpression conjunct1 = BoolExpression.True; BoolExpression conjunct2 = BoolExpression.True; BoolExpression whereClause = null; switch (opType) { case CellTreeOpType.IJ: // Project[D1, D2, A, B, C] Select[cond1 and cond2] (T) // We simply merge the two lists of booleans -- no conjuct is added // conjunct1 and conjunct2 don't change // query1.WhereCaluse AND query2.WhereCaluse Debug.Assert(g1 == null && g2 == null, "IJ does not affect g1 and g2"); whereClause = BoolExpression.CreateAnd(query1.WhereClause, query2.WhereClause); break; case CellTreeOpType.LOJ: // conjunct1 does not change since D1 remains as is // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // D1 does not change. New d2 is the list of booleans expressions // for query2 ANDed with g2 AND query2.WhereClause Debug.Assert(g1 == null, "LOJ does not affect g1"); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); // Just query1's whereclause whereClause = query1.WhereClause; break; case CellTreeOpType.FOJ: case CellTreeOpType.Union: // Project[(expr1 and cond1 and G1) as D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // New D1 is a list -- newD1 = D1 AND query1.WhereClause AND g1 // New D1 is a list -- newD2 = D2 AND query2.WhereClause AND g2 conjunct1 = BoolExpression.CreateAnd(query1.WhereClause, g1); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); // The new whereClause -- g1 AND query1.WhereCaluse OR g2 AND query2.WhereClause whereClause = BoolExpression.CreateOr(BoolExpression.CreateAnd(query1.WhereClause, g1), BoolExpression.CreateAnd(query2.WhereClause, g2)); break; case CellTreeOpType.LASJ: // conjunct1 does not change since D1 remains as is // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // D1 does not change. New d2 is the list of booleans expressions // for query2 ANDed with g2 AND NOT query2.WhereClause Debug.Assert(g1 == null, "LASJ does not affect g1"); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); whereClause = BoolExpression.CreateAnd(query1.WhereClause, BoolExpression.CreateNot(conjunct2)); break; default: Debug.Fail("Unsupported operator"); break; } // Create the various remapped parts for the cell query -- // boolean expressions, merged slots, whereclause, duplicate // elimination, join tree List<BoolExpression> boolExprs = MergeBoolExpressions(query1, query2, conjunct1, conjunct2, opType); //BoolExpression.RemapBools(boolExprs, remap); ProjectedSlot[] mergedSlots; if (false == ProjectedSlot.TryMergeRemapSlots(query1.ProjectedSlots, query2.ProjectedSlots, out mergedSlots)) { // merging failed because two different right slots go to same left slot return false; } whereClause = whereClause.RemapBool(remap); CellQuery.SelectDistinct elimDupl = MergeDupl(query1.SelectDistinctFlag, query2.SelectDistinctFlag); whereClause.ExpensiveSimplify(); mergedQuery = new CellQuery(mergedSlots, whereClause, boolExprs, elimDupl, newRoot); return true; }
internal abstract DomainBoolExpr GetDomainBoolExpression(MemberDomainMap domainMap);
/// <summary> /// Fixes the range of the literal using the new values provided in <paramref name="range"/> and returns a boolean expression corresponding to the new value. /// </summary> internal abstract DomainBoolExpr FixRange(Set <Constant> range, MemberDomainMap memberDomainMap);
// requires: this domainMap has been created for the C-side // effects: Fixes the mergedDomain map in this by merging entries // available in updateDomainMap internal static void PropagateUpdateDomainToQueryDomain(IEnumerable <Cell> cells, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap) { foreach (Cell cell in cells) { CellQuery cQuery = cell.CQuery; CellQuery sQuery = cell.SQuery; for (int i = 0; i < cQuery.NumProjectedSlots; i++) { MemberProjectedSlot cSlot = cQuery.ProjectedSlotAt(i) as MemberProjectedSlot; MemberProjectedSlot sSlot = sQuery.ProjectedSlotAt(i) as MemberProjectedSlot; if (cSlot == null || sSlot == null) { continue; } // Get the domain for sSlot and merge with cSlot's MemberPath cPath = cSlot.MemberPath; MemberPath sPath = sSlot.MemberPath; CellConstantSet cDomain = queryDomainMap.GetDomainInternal(cPath); CellConstantSet sDomain = updateDomainMap.GetDomainInternal(sPath); // skip NULL because if c-side member is nullable, it's already there, and otherwise can't be taken // skip negated because negated values are translated in a special way cDomain.Unite(sDomain.Where(constant => !constant.IsNull() && !(constant is NegatedConstant))); if (updateDomainMap.IsConditionMember(sPath) && !queryDomainMap.IsConditionMember(cPath)) { // record this member so KB knows we have to generate constraints for it queryDomainMap.m_projectedConditionMembers.Add(cPath); } } } ExpandNegationsInDomainMap(queryDomainMap.m_conditionDomainMap); ExpandNegationsInDomainMap(queryDomainMap.m_nonConditionDomainMap); }
internal void FixDomainMap(MemberDomainMap domainMap) { Debug.Assert(domainMap != null, "Member domain map is not set"); m_tree = FixRangeVisitor.FixRange(m_tree, domainMap); }
/// <summary> /// Find the Foreign Key Associations that relate EntitySets used in these left cell wrappers and /// add any equivalence facts between sets implied by 1:1 associations. /// We can collect other implication facts but we don't have a scenario that needs them( yet ). /// </summary> /// <param name="rightKB"></param> /// <param name="rightDomainMap"></param> private void CreateConstraintsForForeignKeyAssociationsAffectingThisWarapper(FragmentQueryKB rightKB, MemberDomainMap rightDomainMap) { //First find the entity types of the sets in these cell wrappers. var entityTypes = m_cellWrappers.Select(it => it.RightExtent).OfType<EntitySet>().Select(it => it.ElementType); //Get all the foreign key association sets in these entity sets var allForeignKeyAssociationSets = this.m_entityContainerMapping.EdmEntityContainer.BaseEntitySets.OfType<AssociationSet>().Where(it => it.ElementType.IsForeignKey); //Find all the foreign key associations that have corresponding sets var oneToOneForeignKeyAssociationsForThisWrapper = allForeignKeyAssociationSets.Select(it => it.ElementType); //Find all the 1:1 associations from the above list oneToOneForeignKeyAssociationsForThisWrapper = oneToOneForeignKeyAssociationsForThisWrapper.Where(it => (it.AssociationEndMembers.All(endMember => endMember.RelationshipMultiplicity == RelationshipMultiplicity.One))); //Filter the 1:1 foreign key associations to the ones relating the sets used in these cell wrappers. oneToOneForeignKeyAssociationsForThisWrapper = oneToOneForeignKeyAssociationsForThisWrapper.Where(it => (it.AssociationEndMembers.All(endMember => entityTypes.Contains(endMember.GetEntityType())))); //filter foreign key association sets to the sets that are 1:1 and affecting this wrapper. var oneToOneForeignKeyAssociationSetsForThisWrapper = allForeignKeyAssociationSets.Where(it => oneToOneForeignKeyAssociationsForThisWrapper.Contains(it.ElementType)); //Collect the facts for the foreign key association sets that are 1:1 and affecting this wrapper foreach (var assocSet in oneToOneForeignKeyAssociationSetsForThisWrapper) { rightKB.CreateEquivalenceConstraintForOneToOneForeignKeyAssociation(assocSet, rightDomainMap, m_edmItemCollection); } }
internal ViewgenContext(ViewTarget viewTarget, EntitySetBase extent, IEnumerable<Cell> extentCells, CqlIdentifiers identifiers, ConfigViewGenerator config, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap, StorageEntityContainerMapping entityContainerMapping) { foreach (Cell cell in extentCells) { Debug.Assert(extent.Equals(cell.GetLeftQuery(viewTarget).Extent)); Debug.Assert(cell.CQuery.NumProjectedSlots == cell.SQuery.NumProjectedSlots); } m_extent = extent; m_viewTarget = viewTarget; m_config = config; m_edmItemCollection = entityContainerMapping.StorageMappingItemCollection.EdmItemCollection; m_entityContainerMapping = entityContainerMapping; m_identifiers = identifiers; // create a copy of updateDomainMap so generation of query views later on is not affected // it is modified in QueryRewriter.AdjustMemberDomainsForUpdateViews updateDomainMap = updateDomainMap.MakeCopy(); // Create a signature generator that handles all the // multiconstant work and generating the signatures MemberDomainMap domainMap = viewTarget == ViewTarget.QueryView ? queryDomainMap : updateDomainMap; m_memberMaps = new MemberMaps(viewTarget, MemberProjectionIndex.Create(extent, m_edmItemCollection), queryDomainMap, updateDomainMap); // Create left fragment KB: includes constraints for the extent to be constructed FragmentQueryKB leftKB = new FragmentQueryKB(); leftKB.CreateVariableConstraints(extent, domainMap, m_edmItemCollection); m_leftFragmentQP = new FragmentQueryProcessor(leftKB); m_rewritingCache = new Dictionary<FragmentQuery, Tile<FragmentQuery>>( FragmentQuery.GetEqualityComparer(m_leftFragmentQP)); // Now using the signatures, create new cells such that // "extent's" query (C or S) is described in terms of multiconstants if (!CreateLeftCellWrappers(extentCells, viewTarget)) { return; } // Create right fragment KB: includes constraints for all extents and association roles of right queries FragmentQueryKB rightKB = new FragmentQueryKB(); MemberDomainMap rightDomainMap = viewTarget == ViewTarget.QueryView ? updateDomainMap : queryDomainMap; foreach (LeftCellWrapper leftCellWrapper in m_cellWrappers) { EntitySetBase rightExtent = leftCellWrapper.RightExtent; rightKB.CreateVariableConstraints(rightExtent, rightDomainMap, m_edmItemCollection); rightKB.CreateAssociationConstraints(rightExtent, rightDomainMap, m_edmItemCollection); } if (m_viewTarget == ViewTarget.UpdateView) { CreateConstraintsForForeignKeyAssociationsAffectingThisWarapper(rightKB, rightDomainMap); } m_rightFragmentQP = new FragmentQueryProcessor(rightKB); // Check for concurrency control tokens if (m_viewTarget == ViewTarget.QueryView) { CheckConcurrencyControlTokens(); } // For backward compatibility - // order wrappers by increasing domain size, decreasing number of attributes m_cellWrappers.Sort(LeftCellWrapper.Comparer); }
// requires: The CellConstantDomains in the OneOfConsts of the where // clause are partially done // effects: Given the domains of different variables in domainMap, // fixes the whereClause of this such that all the // CellConstantDomains in OneOfConsts are complete internal void UpdateWhereClause(MemberDomainMap domainMap) { var atoms = new List<BoolExpression>(); foreach (var atom in WhereClause.Atoms) { var literal = atom.AsLiteral; var restriction = literal as MemberRestriction; Debug.Assert(restriction != null, "All bool literals must be OneOfConst at this point"); // The oneOfConst needs to be fixed with the new possible values from the domainMap. var possibleValues = domainMap.GetDomain(restriction.RestrictedMemberSlot.MemberPath); var newOneOf = restriction.CreateCompleteMemberRestriction(possibleValues); // Prevent optimization of single constraint e.g: "300 in (300)" // But we want to optimize type constants e.g: "category in (Category)" // To prevent optimization of bool expressions we add a Sentinel OneOF var scalarConst = restriction as ScalarRestriction; var addSentinel = scalarConst != null && !scalarConst.Domain.Contains(Constant.Null) && !scalarConst.Domain.Contains(Constant.NotNull) && !scalarConst.Domain.Contains(Constant.Undefined); if (addSentinel) { domainMap.AddSentinel(newOneOf.RestrictedMemberSlot.MemberPath); } atoms.Add(BoolExpression.CreateLiteral(newOneOf, domainMap)); if (addSentinel) { domainMap.RemoveSentinel(newOneOf.RestrictedMemberSlot.MemberPath); } } // We create a new whereClause that has the memberDomainMap set if (atoms.Count > 0) { m_whereClause = BoolExpression.CreateAnd(atoms.ToArray()); } }
// requires: this domainMap has been created for the C-side // effects: Fixes the mergedDomain map in this by merging entries // available in updateDomainMap internal static void PropagateUpdateDomainToQueryDomain(IEnumerable<Cell> cells, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap) { foreach (Cell cell in cells) { CellQuery cQuery = cell.CQuery; CellQuery sQuery = cell.SQuery; for (int i = 0; i < cQuery.NumProjectedSlots; i++) { MemberProjectedSlot cSlot = cQuery.ProjectedSlotAt(i) as MemberProjectedSlot; MemberProjectedSlot sSlot = sQuery.ProjectedSlotAt(i) as MemberProjectedSlot; if (cSlot == null || sSlot == null) { continue; } // Get the domain for sSlot and merge with cSlot's MemberPath cPath = cSlot.MemberPath; MemberPath sPath = sSlot.MemberPath; CellConstantSet cDomain = queryDomainMap.GetDomainInternal(cPath); CellConstantSet sDomain = updateDomainMap.GetDomainInternal(sPath); // skip NULL because if c-side member is nullable, it's already there, and otherwise can't be taken // skip negated because negated values are translated in a special way cDomain.Unite(sDomain.Where(constant => !constant.IsNull() && !(constant is NegatedConstant))); if (updateDomainMap.IsConditionMember(sPath) && !queryDomainMap.IsConditionMember(cPath)) { // record this member so KB knows we have to generate constraints for it queryDomainMap.m_projectedConditionMembers.Add(cPath); } } } ExpandNegationsInDomainMap(queryDomainMap.m_conditionDomainMap); ExpandNegationsInDomainMap(queryDomainMap.m_nonConditionDomainMap); }
internal static FragmentQuery CreateMemberConditionQuery( MemberPath currentPath, Constant domainValue, IEnumerable<MemberPath> keyAttributes, MemberDomainMap domainMap) { // construct WHERE clause for this value var domainWhereClause = FragmentQuery.CreateMemberCondition(currentPath, domainValue, domainMap); // get a rewriting for CASE statements by not requesting any attributes beyond key var attributes = keyAttributes; if (domainValue is NegatedConstant) { // we need the attribute value attributes = keyAttributes.Concat(new[] { currentPath }); } return FragmentQuery.Create(attributes, domainWhereClause); }
private static IEnumerable<MemberPath> GetConditionalScalarMembers( EdmType edmType, MemberPath currentPath, MemberDomainMap domainMap) { return currentPath.GetMembers(edmType, true /* isScalar */, true /* isConditional */, null /* isPartOfKey */, domainMap); }
// effects: Creates a boolean expression based on expr internal BoolExpression(DomainBoolExpr expr, MemberDomainMap memberDomainMap) { m_tree = expr; m_memberDomainMap = memberDomainMap; }