1 //---------------------------------------------------------------------
2 // <copyright file="ErrorPatternMatcher.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Data.Common.Utils;
12 using System.Data.Entity;
13 using System.Data.Mapping.ViewGeneration.QueryRewriting;
14 using System.Data.Mapping.ViewGeneration.Structures;
15 using System.Data.Mapping.ViewGeneration.Utils;
16 using System.Data.Metadata.Edm;
17 using System.Diagnostics;
21 namespace System.Data.Mapping.ViewGeneration.Validation
23 using CompositeCondition = Dictionary<MemberPath, Set<Constant>>;
24 delegate bool LCWComparer(FragmentQuery query1, FragmentQuery query2);
26 internal class ErrorPatternMatcher
29 private ViewgenContext m_viewgenContext;
30 private MemberDomainMap m_domainMap;
31 private IEnumerable<MemberPath> m_keyAttributes;
32 private ErrorLog m_errorLog;
33 private int m_originalErrorCount;
34 private const int NUM_PARTITION_ERR_TO_FIND = 5;
38 private ErrorPatternMatcher(ViewgenContext context, MemberDomainMap domainMap, ErrorLog errorLog)
40 m_viewgenContext = context;
41 m_domainMap = domainMap;
42 m_keyAttributes = MemberPath.GetKeyMembers(context.Extent, domainMap);
43 m_errorLog = errorLog;
44 m_originalErrorCount = m_errorLog.Count;
47 public static bool FindMappingErrors(ViewgenContext context, MemberDomainMap domainMap, ErrorLog errorLog)
49 //Can't get here if Update Views have validation disabled
50 Debug.Assert(context.ViewTarget == ViewTarget.QueryView || context.Config.IsValidationEnabled);
52 if (context.ViewTarget == ViewTarget.QueryView && !context.Config.IsValidationEnabled)
54 return false; // Rules for QV under no validation are different
57 ErrorPatternMatcher matcher = new ErrorPatternMatcher(context, domainMap, errorLog);
59 matcher.MatchMissingMappingErrors();
60 matcher.MatchConditionErrors();
61 matcher.MatchSplitErrors();
63 if (matcher.m_errorLog.Count == matcher.m_originalErrorCount)
64 { //this will generate redundant errors if one of the above routine finds an error
65 // so execute it only when we dont have any other errors
66 matcher.MatchPartitionErrors();
69 if (matcher.m_errorLog.Count > matcher.m_originalErrorCount)
71 ExceptionHelpers.ThrowMappingException(matcher.m_errorLog, matcher.m_viewgenContext.Config);
78 #region Error Matching Routines
82 /// Finds Types (possibly without any members) that have no mapping specified
84 private void MatchMissingMappingErrors()
86 if (m_viewgenContext.ViewTarget == ViewTarget.QueryView)
88 //Find all types for the given EntitySet
89 Set<EdmType> unmapepdTypesInExtent = new Set<EdmType>(MetadataHelper.GetTypeAndSubtypesOf(m_viewgenContext.Extent.ElementType, m_viewgenContext.EdmItemCollection, false /*isAbstract*/));
91 //Figure out which type has no Cell mapped to it
92 foreach (var fragment in m_viewgenContext.AllWrappersForExtent)
94 foreach (Cell cell in fragment.Cells)
96 foreach (var restriction in cell.CQuery.Conditions)
98 foreach (var cellConst in restriction.Domain.Values)
100 //if there is a mapping to this type...
101 TypeConstant typeConst = cellConst as TypeConstant;
102 if (typeConst != null)
104 unmapepdTypesInExtent.Remove(typeConst.EdmType);
111 //We are left with a type that has no mapping
112 if (unmapepdTypesInExtent.Count > 0)
114 //error unmapped type
115 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternMissingMappingError,
116 Strings.ViewGen_Missing_Type_Mapping(BuildCommaSeparatedErrorString<EdmType>(unmapepdTypesInExtent)), m_viewgenContext.AllWrappersForExtent, ""));
121 private static bool HasNotNullCondition(CellQuery cellQuery, MemberPath member)
123 foreach (MemberRestriction condition in cellQuery.GetConjunctsFromWhereClause())
125 if (condition.RestrictedMemberSlot.MemberPath.Equals(member))
127 if (condition.Domain.Values.Contains(Constant.NotNull))
132 //Not Null may have been optimized into NOT(1, 2, NULL). SO look into negated cell constants
133 foreach (NegatedConstant negatedConst in condition.Domain.Values.Select(cellConstant => cellConstant as NegatedConstant).Where(negated => negated != null))
135 if (negatedConst.Elements.Contains(Constant.Null))
145 private static bool IsMemberPartOfNotNullCondition(IEnumerable<LeftCellWrapper> wrappers, MemberPath leftMember, ViewTarget viewTarget)
148 foreach (var leftCellWrapper in wrappers)
150 CellQuery leftCellQuery = leftCellWrapper.OnlyInputCell.GetLeftQuery(viewTarget);
152 if (HasNotNullCondition(leftCellQuery, leftMember))
157 //Now figure out corresponding right side MemberPath
158 CellQuery rightCellQuery = leftCellWrapper.OnlyInputCell.GetRightQuery(viewTarget);
159 int indexOfMemberInProjection = leftCellQuery.GetProjectedMembers().TakeWhile(path => !path.Equals(leftMember)).Count();
161 //Member with condition is projected, so check opposite CellQuery's condition
162 if (indexOfMemberInProjection < leftCellQuery.GetProjectedMembers().Count())
164 MemberPath rightmember = ((MemberProjectedSlot)rightCellQuery.ProjectedSlotAt(indexOfMemberInProjection)).MemberPath;
166 if (HasNotNullCondition(rightCellQuery, rightmember))
177 /// Finds errors related to splitting Conditions
178 /// 1. Condition value is repeated across multiple types
179 /// 2. A Column/attribute is mapped but also used as a condition
181 private void MatchConditionErrors()
183 List<LeftCellWrapper> leftCellWrappers = m_viewgenContext.AllWrappersForExtent;
185 //Stores violating Discriminator (condition member) so that we dont repeat the same error
186 Set<MemberPath> mappedConditionMembers = new Set<MemberPath>();
188 //Both of these data-structs help in finding duplicate conditions
189 Set<CompositeCondition> setOfconditions = new Set<CompositeCondition>(new ConditionComparer());
190 Dictionary<CompositeCondition, LeftCellWrapper> firstLCWForCondition = new Dictionary<CompositeCondition, LeftCellWrapper>(new ConditionComparer());
192 foreach (var leftCellWrapper in leftCellWrappers)
194 CompositeCondition condMembersValues = new CompositeCondition();
196 CellQuery cellQuery = leftCellWrapper.OnlyInputCell.GetLeftQuery(m_viewgenContext.ViewTarget);
198 foreach (MemberRestriction condition in cellQuery.GetConjunctsFromWhereClause())
200 MemberPath memberPath = condition.RestrictedMemberSlot.MemberPath;
202 if (!m_domainMap.IsConditionMember(memberPath))
207 ScalarRestriction scalarCond = condition as ScalarRestriction;
208 //Check for mapping of Scalar member condition, ignore type conditions
209 if (scalarCond != null &&
210 !mappedConditionMembers.Contains(memberPath) && /* prevents duplicate errors */
211 !leftCellWrapper.OnlyInputCell.CQuery.WhereClause.Equals(leftCellWrapper.OnlyInputCell.SQuery.WhereClause) && /* projection allowed when both conditions are equal */
212 !IsMemberPartOfNotNullCondition(leftCellWrappers, memberPath, m_viewgenContext.ViewTarget))
214 //This member should not be mapped
215 CheckThatConditionMemberIsNotMapped(memberPath, leftCellWrappers, mappedConditionMembers);
218 //If a not-null condition is specified on a nullable column,
219 //check that the property it is mapped to in the fragment is non-nullable,
220 //unless there is a not null condition on the property that is being mapped it self.
221 //Otherwise return an error.
222 if (m_viewgenContext.ViewTarget == ViewTarget.UpdateView)
224 if (scalarCond != null &&
225 memberPath.IsNullable && IsMemberPartOfNotNullCondition(new LeftCellWrapper[] { leftCellWrapper }, memberPath, m_viewgenContext.ViewTarget))
227 MemberPath rightMemberPath = GetRightMemberPath(memberPath, leftCellWrapper);
228 if (rightMemberPath != null && rightMemberPath.IsNullable &&
229 !IsMemberPartOfNotNullCondition(new LeftCellWrapper[] { leftCellWrapper }, rightMemberPath, m_viewgenContext.ViewTarget))
231 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError,
232 Strings.Viewgen_ErrorPattern_NotNullConditionMappedToNullableMember(
233 memberPath, rightMemberPath
234 ), leftCellWrapper.OnlyInputCell, ""));
239 //CheckForDuplicateConditionValue
240 //discover a composite condition of the form {path1=x, path2=y, ...}
241 foreach (var element in condition.Domain.Values)
243 Set<Constant> values;
244 //if not in the dict, add it
245 if (!condMembersValues.TryGetValue(memberPath, out values))
247 values = new Set<Constant>(Constant.EqualityComparer);
248 condMembersValues.Add(memberPath, values);
253 } //foreach condition
255 if (condMembersValues.Count > 0) //it is possible that there are no condition members
257 //Check if the composite condition has been encountered before
258 if (setOfconditions.Contains(condMembersValues))
260 //Extents may be Equal on right side (e.g: by some form of Refconstraint)
261 if (!RightSideEqual(firstLCWForCondition[condMembersValues], leftCellWrapper))
263 //error duplicate conditions
264 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError,
265 Strings.Viewgen_ErrorPattern_DuplicateConditionValue(
266 BuildCommaSeparatedErrorString<MemberPath>(condMembersValues.Keys)
268 ToIEnum(firstLCWForCondition[condMembersValues].OnlyInputCell, leftCellWrapper.OnlyInputCell), ""));
273 setOfconditions.Add(condMembersValues);
275 //Remember which cell the condition came from.. used for error reporting
276 firstLCWForCondition.Add(condMembersValues, leftCellWrapper);
279 } //foreach fragment related to the Extent we are working on
283 private MemberPath GetRightMemberPath(MemberPath conditionMember,LeftCellWrapper leftCellWrapper)
285 CellQuery rightCellQuery = leftCellWrapper.OnlyInputCell.GetRightQuery(ViewTarget.QueryView);
286 var projectPositions = rightCellQuery.GetProjectedPositions(conditionMember);
287 //Make the case simple. If the member is mapped more than once in the same cell wrapper
288 //we are not going try and guess the pattern
289 if (projectPositions.Count != 1)
293 int firstProjectedPosition = projectPositions.First();
294 CellQuery leftCellQuery = leftCellWrapper.OnlyInputCell.GetLeftQuery(ViewTarget.QueryView);
295 return ((MemberProjectedSlot)leftCellQuery.ProjectedSlotAt(firstProjectedPosition)).MemberPath;
299 /// When we are dealing with an update view, this method
300 /// finds out if the given Table is mapped to different EntitySets
302 private void MatchSplitErrors()
304 List<LeftCellWrapper> leftCellWrappers = m_viewgenContext.AllWrappersForExtent;
306 //Check that the given Table is mapped to only one EntitySet (avoid AssociationSets)
307 var nonAssociationWrappers = leftCellWrappers.Where(r => !(r.LeftExtent is AssociationSet) && !(r.RightCellQuery.Extent is AssociationSet));
309 if (m_viewgenContext.ViewTarget == ViewTarget.UpdateView && nonAssociationWrappers.Any())
311 LeftCellWrapper firstLeftCWrapper = nonAssociationWrappers.First();
312 EntitySetBase rightExtent = firstLeftCWrapper.RightCellQuery.Extent;
314 foreach (var leftCellWrapper in nonAssociationWrappers)
316 //!(leftCellWrapper.RightCellQuery.Extent is AssociationSet) &&
317 if (!leftCellWrapper.RightCellQuery.Extent.EdmEquals(rightExtent))
319 //A Table may be mapped to two extents but the extents may be Equal (by some form of Refconstraint)
320 if (!RightSideEqual(leftCellWrapper, firstLeftCWrapper))
323 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternSplittingError,
324 Strings.Viewgen_ErrorPattern_TableMappedToMultipleES(leftCellWrapper.LeftExtent.ToString(), leftCellWrapper.RightCellQuery.Extent.ToString(), rightExtent.ToString()),
325 leftCellWrapper.Cells.First(), ""));
333 /// Finds out whether fragments (partitions) violate constraints that would produce an invalid mapping.
334 /// We compare equality/disjointness/containment for all 2-combinations of fragments.
335 /// Error is reported if given relationship on S side is not maintained on the C side.
336 /// If we know nothing about S-side then any relationship on C side is valid.
338 private void MatchPartitionErrors()
340 List<LeftCellWrapper> mappingFragments = m_viewgenContext.AllWrappersForExtent;
342 //for every 2-combination nC2 (n choose 2)
344 foreach (var fragment1 in mappingFragments)
346 foreach (var fragment2 in mappingFragments.Skip(++i))
348 FragmentQuery rightFragmentQuery1 = CreateRightFragmentQuery(fragment1);
349 FragmentQuery rightFragmentQuery2 = CreateRightFragmentQuery(fragment2);
351 bool isSDisjoint = CompareS(ComparisonOP.IsDisjointFrom, m_viewgenContext, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2);
352 bool isCDisjoint = CompareC(ComparisonOP.IsDisjointFrom, m_viewgenContext, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2);
369 //Figure out more info for accurate message
370 is1SubsetOf2_C = CompareC(ComparisonOP.IsContainedIn, m_viewgenContext, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2);
371 is2SubsetOf1_C = CompareC(ComparisonOP.IsContainedIn, m_viewgenContext, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1);
372 isCEqual = is1SubsetOf2_C && is2SubsetOf1_C;
374 StringBuilder errorString = new StringBuilder();
376 if (isCEqual) //equal
378 //MSG: These two fragments are disjoint on the S-side but equal on the C-side.
379 // Ensure disjointness on C-side by mapping them to different types within the same EntitySet
380 // or by mapping them to the same type but with a C-side discriminator.
382 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Eq);
384 else if (is1SubsetOf2_C || is2SubsetOf1_C)
386 //Really overlap is not accurate term (should be contianed in or subset of), but its easiest to read.
388 if (CSideHasDifferentEntitySets(fragment1, fragment2))
390 //MSG: These two fragments are disjoint on the S-side but overlap on the C-side via a Referential constraint.
391 // Ensure disjointness on C-side by mapping them to different types within the same EntitySet
392 // or by mapping them to the same type but with a C-side discriminator.
394 //TestCase (Not possible because all PKs must be mapped)
395 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Subs_Ref);
399 //MSG: These two fragments are disjoint on the S-side but overlap on the C-side.
400 // Ensure disjointness on C-side. You may be using IsTypeOf() quantifier to
401 // map multiple types within one of these fragments.
403 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Subs);
406 else //relationship is unknown
408 //MSG: These two fragments are disjoint on the S-side but not so on the C-side.
409 // Ensure disjointness on C-side by mapping them to different types within the same EntitySet
410 // or by mapping them to the same type but with a C-side discriminator.
413 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Unk);
416 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), ""));
418 if (FoundTooManyErrors())
426 is1SubsetOf2_C = CompareC(ComparisonOP.IsContainedIn, m_viewgenContext, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2);
427 is2SubsetOf1_C = CompareC(ComparisonOP.IsContainedIn, m_viewgenContext, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1);
429 is1SubsetOf2_S = CompareS(ComparisonOP.IsContainedIn, m_viewgenContext, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2);
430 is2SubsetOf1_S = CompareS(ComparisonOP.IsContainedIn, m_viewgenContext, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1);
432 isCEqual = is1SubsetOf2_C && is2SubsetOf1_C;
433 isSEqual = is1SubsetOf2_S && is2SubsetOf1_S;
438 if (isCEqual) //c-side equal
445 StringBuilder errorString = new StringBuilder();
449 //MSG: These two fragments are equal on the S-side but disjoint on the C-side.
450 // Either partition the S-side by adding a condition or remove any C-side conditions along with resulting redundant mapping fragments.
451 // You may also map these two disjoint C-side partitions to different tables.
453 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Disj);
455 else if (is1SubsetOf2_C || is2SubsetOf1_C)
457 if (CSideHasDifferentEntitySets(fragment1, fragment2))
459 //MSG: These two fragments are equal on the S-side but overlap on the C-side.
460 // It is likely that you have not added Referential Integrity constriaint for all Key attributes of both EntitySets.
461 // Doing so would ensure equality on the C-side.
462 //TestCase (Not possible, right?)
463 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Subs_Ref);
467 //MSG: These two fragments are equal on the S-side but overlap on the C-side.
468 // If you are using IsTypeOf() quantifier ensure both mapping fragments capture same types on the C-side.
469 // Otherwise you may have intended to partition the S-side.
472 //Check for the specific case
473 //where there are mapping fragments with different types on C side
474 //mapped to same table on the Store side but not all the fragments have
475 //a condition. Ignore the cases where any of the fragments have C side conditions.
476 if (fragment1.LeftExtent.Equals(fragment2.LeftExtent))
478 bool firstCellWrapperHasCondition;
479 List<EdmType> edmTypesForFirstCellWrapper;
480 bool secondCellWrapperHasCondition;
481 List<EdmType> edmTypesForSecondCellWrapper;
482 GetTypesAndConditionForWrapper(fragment1, out firstCellWrapperHasCondition, out edmTypesForFirstCellWrapper);
483 GetTypesAndConditionForWrapper(fragment2, out secondCellWrapperHasCondition, out edmTypesForSecondCellWrapper);
484 if (!firstCellWrapperHasCondition && !secondCellWrapperHasCondition)
486 if (((edmTypesForFirstCellWrapper.Except(edmTypesForSecondCellWrapper)).Count() != 0 )
487 || ((edmTypesForSecondCellWrapper.Except(edmTypesForFirstCellWrapper)).Count() != 0 ))
489 if (!CheckForStoreConditions(fragment1) || !CheckForStoreConditions(fragment2))
491 IEnumerable<string> edmTypesForErrorString = edmTypesForFirstCellWrapper.Select(it => it.FullName).Union(edmTypesForSecondCellWrapper.Select(it => it.FullName));
492 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError,
493 Strings.Viewgen_ErrorPattern_Partition_MultipleTypesMappedToSameTable_WithoutCondition(
494 StringUtil.ToCommaSeparatedString(edmTypesForErrorString), fragment1.LeftExtent
495 ), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), ""));
502 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Subs);
507 //S-side equal, C-side Unknown
508 if (!IsQueryView() &&
509 (fragment1.OnlyInputCell.CQuery.Extent is AssociationSet ||
510 fragment2.OnlyInputCell.CQuery.Extent is AssociationSet))
512 //one side is an association set
513 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Unk_Association);
518 //MSG: These two fragments are equal on the S-side but not so on the C-side.
519 // Try adding an Association with Referntial Integrity constraint if they are
520 // mapped to different EntitySets in order to make theme equal on the C-side.
521 //TestCase (no need, Table mapped to multiple ES tests cover this scenario)
522 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Unk);
526 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), ""));
528 if (FoundTooManyErrors())
534 else if (is1SubsetOf2_S || is2SubsetOf1_S) //proper subset - note: else if ensures inverse need not be checked
536 //C-side proper subset (c side must not be equal)
537 if ((is1SubsetOf2_S && is1SubsetOf2_C == true && !(is2SubsetOf1_C == true)) || (is2SubsetOf1_S && is2SubsetOf1_C == true && !(is1SubsetOf2_C == true)))
544 StringBuilder errorString = new StringBuilder();
548 //MSG: One of the fragments is a subset of the other on the S-side but they are disjoint on the C-side.
549 // If you intended overlap on the S-side ensure they have similar relationship on teh C-side.
550 // You may need to use IsTypeOf() quantifier or loosen conditions in one of the fragments.
552 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Disj);
554 else if (isCEqual) //equal
556 //MSG: One of the fragments is a subset of the other on the S-side but they are equal on the C-side.
557 // If you intended overlap on the S-side ensure they have similar relationship on teh C-side.
561 if (CSideHasDifferentEntitySets(fragment1, fragment2))
563 // If they are equal via a Referential integrity constraint try making one a subset of the other by
564 // not including all primary keys in the constraint.
565 //TestCase (Not possible)
566 errorString.Append(" " + Strings.Viewgen_ErrorPattern_Partition_Sub_Eq_Ref);
570 // You may need to modify conditions in one of the fragments.
572 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Eq);
577 //MSG: One of the fragments is a subset of the other on the S-side but they are disjoint on the C-side.
578 // If you intended overlap on the S-side ensure they have similar relationship on teh C-side.
579 //TestCase (no need, Table mapped to multiple ES tests cover this scenario)
580 errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Unk);
583 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), ""));
585 if (FoundTooManyErrors())
591 //else unknown relationship on the S-side
593 } //end looping over every 2-combination of fragment
597 /// Gets the types on the Edm side mapped in this fragment wrapper.
598 /// It also returns an out parameter indicating whether there were any C side conditions.
600 private void GetTypesAndConditionForWrapper(LeftCellWrapper wrapper, out bool hasCondition, out List<EdmType> edmTypes)
602 hasCondition = false;
603 edmTypes = new List<EdmType>();
604 //Figure out which type has no Cell mapped to it
605 foreach (Cell cell in wrapper.Cells)
607 foreach (var restriction in cell.CQuery.Conditions)
609 foreach (var cellConst in restriction.Domain.Values)
611 //if there is a mapping to this type...
612 TypeConstant typeConst = cellConst as TypeConstant;
613 if (typeConst != null)
615 edmTypes.Add(typeConst.EdmType);
627 /// Return true if there were any Store conditions on this cell wrapper.
629 /// <param name="wrapper"></param>
630 /// <returns></returns>
631 private bool CheckForStoreConditions(LeftCellWrapper wrapper)
633 return wrapper.Cells.SelectMany(c => c.SQuery.Conditions).Any();
637 private void CheckThatConditionMemberIsNotMapped(MemberPath conditionMember, List<LeftCellWrapper> mappingFragments, Set<MemberPath> mappedConditionMembers)
640 //Make sure memberPath is not mapped (in any other cells)
641 foreach (var anotherFragment in mappingFragments)
643 foreach (var anotherCell in anotherFragment.Cells)
645 CellQuery anotherCellQuery = anotherCell.GetLeftQuery(m_viewgenContext.ViewTarget);
646 if (anotherCellQuery.GetProjectedMembers().Contains(conditionMember))
648 mappedConditionMembers.Add(conditionMember);
649 //error condition memer is projected somewhere
650 m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError, Strings.Viewgen_ErrorPattern_ConditionMemberIsMapped(conditionMember.ToString()), anotherCell, ""));
658 private bool FoundTooManyErrors()
660 return (m_errorLog.Count > m_originalErrorCount + NUM_PARTITION_ERR_TO_FIND);
663 #region Private Helpers
665 private string BuildCommaSeparatedErrorString<T>(IEnumerable<T> members)
667 StringBuilder builder = new StringBuilder();
669 var firstMember = members.First();
670 foreach (var member in members)
672 if (!member.Equals(firstMember))
674 builder.Append(", ");
676 builder.Append("'" + member.ToString() + "'");
678 return builder.ToString();
681 private bool CSideHasDifferentEntitySets(LeftCellWrapper a, LeftCellWrapper b)
685 return a.LeftExtent == b.LeftExtent;
689 return a.RightCellQuery == b.RightCellQuery;
693 private bool CompareC(ComparisonOP op, ViewgenContext context, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2)
695 return Compare(true /*lookingForCSide*/, op, context, leftWrapper1, leftWrapper2, rightQuery1, rightQuery2);
698 private bool CompareS(ComparisonOP op, ViewgenContext context, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2)
700 return Compare(false/*lookingForCSide*/, op, context, leftWrapper1, leftWrapper2, rightQuery1, rightQuery2);
703 private bool Compare(bool lookingForC, ComparisonOP op, ViewgenContext context, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2)
705 LCWComparer comparer;
707 if ((lookingForC && IsQueryView()) || (!lookingForC && !IsQueryView()))
709 if (op == ComparisonOP.IsContainedIn)
711 comparer = context.LeftFragmentQP.IsContainedIn;
713 else if (op == ComparisonOP.IsDisjointFrom)
715 comparer = context.LeftFragmentQP.IsDisjointFrom;
719 Debug.Fail("Unexpected comparison operator, only IsDisjointFrom and IsContainedIn are expected");
723 return comparer(leftWrapper1.FragmentQuery, leftWrapper2.FragmentQuery);
727 if (op == ComparisonOP.IsContainedIn)
729 comparer = context.RightFragmentQP.IsContainedIn;
731 else if (op == ComparisonOP.IsDisjointFrom)
733 comparer = context.RightFragmentQP.IsDisjointFrom;
737 Debug.Fail("Unexpected comparison operator, only IsDisjointFrom and IsContainedIn are expected");
741 return comparer(rightQuery1, rightQuery2);
745 private bool RightSideEqual(LeftCellWrapper wrapper1, LeftCellWrapper wrapper2)
747 FragmentQuery rightFragmentQuery1 = CreateRightFragmentQuery(wrapper1);
748 FragmentQuery rightFragmentQuery2 = CreateRightFragmentQuery(wrapper2);
750 return m_viewgenContext.RightFragmentQP.IsEquivalentTo(rightFragmentQuery1, rightFragmentQuery2);
753 private FragmentQuery CreateRightFragmentQuery(LeftCellWrapper wrapper)
755 return FragmentQuery.Create(wrapper.OnlyInputCell.CellLabel.ToString(), wrapper.CreateRoleBoolean(), wrapper.OnlyInputCell.GetRightQuery(m_viewgenContext.ViewTarget));
758 private IEnumerable<Cell> ToIEnum(Cell one, Cell two)
760 List<Cell> cells = new List<Cell>();
766 private bool IsQueryView()
768 return (m_viewgenContext.ViewTarget == ViewTarget.QueryView);
780 class ConditionComparer : IEqualityComparer<Dictionary<MemberPath, Set<Constant>>>
782 public bool Equals(Dictionary<MemberPath, Set<Constant>> one, Dictionary<MemberPath, Set<Constant>> two)
784 Set<MemberPath> keysOfOne = new Set<MemberPath>(one.Keys, MemberPath.EqualityComparer);
785 Set<MemberPath> keysOfTwo = new Set<MemberPath>(two.Keys, MemberPath.EqualityComparer);
787 if (!keysOfOne.SetEquals(keysOfTwo))
792 foreach (var member in keysOfOne)
794 Set<Constant> constantsOfOne = one[member];
795 Set<Constant> constantsOfTwo = two[member];
797 if (!constantsOfOne.SetEquals(constantsOfTwo))
805 public int GetHashCode(Dictionary<MemberPath, Set<Constant>> obj)
807 StringBuilder builder = new StringBuilder();
808 foreach (var key in obj.Keys)
810 builder.Append(key.ToString());
813 return builder.ToString().GetHashCode();