Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Validation / errorpatternmatcher.cs / 1 / errorpatternmatcher.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Common.Utils; using System.Collections.Generic; using System.Data.Mapping.ViewGeneration.Validation; using System.Data.Mapping.ViewGeneration.Structures; using System.Text; using System.Diagnostics; using System.Collections.ObjectModel; using System.Data.Mapping.ViewGeneration.Utils; using System.Data.Metadata.Edm; using System.Data.Entity; using System.Data.Common.Utils.Boolean; using System.Linq; using System.Data.Mapping.ViewGeneration.QueryRewriting; namespace System.Data.Mapping.ViewGeneration.Validation { using CompositeCondition = Dictionary>; using System.Globalization; delegate bool LCWComparer(FragmentQuery query1, FragmentQuery query2); internal class ErrorPatternMatcher { private CellNormalizer m_normalizer; private MemberDomainMap m_domainMap; private IEnumerable m_keyAttributes; private ErrorLog m_errorLog; private int m_originalErrorCount; private const int NUM_PARTITION_ERR_TO_FIND = 5; #region Constructor private ErrorPatternMatcher(CellNormalizer normalizer, MemberDomainMap domainMap, ErrorLog errorLog) { m_normalizer = normalizer; m_domainMap = domainMap; m_keyAttributes = MemberPath.GetKeyMembers(normalizer.Extent, domainMap, normalizer.Workspace); m_errorLog = errorLog; m_originalErrorCount = m_errorLog.Count; } public static bool FindMappingErrors(CellNormalizer normalizer, MemberDomainMap domainMap, ErrorLog errorLog) { ErrorPatternMatcher matcher = new ErrorPatternMatcher(normalizer, domainMap, errorLog); matcher.MatchMissingMappingErrors(); matcher.MatchConditionErrors(); matcher.MatchSplitErrors(); if (matcher.m_errorLog.Count == matcher.m_originalErrorCount) { //this will generate redundant errors if one of the above routine finds an error // so execute it only when we dont have any other errors matcher.MatchPartitionErrors(); } if (matcher.m_errorLog.Count > matcher.m_originalErrorCount) { ExceptionHelpers.ThrowMappingException(matcher.m_errorLog, matcher.m_normalizer.Config); } return false; } #endregion #region Error Matching Routines /// /// Finds Types (possibly without any members) that have no mapping specified /// private void MatchMissingMappingErrors() { if (m_normalizer.SchemaContext.ViewTarget == ViewTarget.QueryView) { //Find all types for the given EntitySet SetunmapepdTypesInExtent = new Set (MetadataHelper.GetTypeAndSubtypesOf(m_normalizer.Extent.ElementType, m_normalizer.Workspace, false /*isAbstract*/)); //Figure out which type has no Cell mapped to it foreach (var fragment in m_normalizer.AllWrappersForExtent) { foreach (Cell cell in fragment.Cells) { foreach( var oneOfConst in cell.CQuery.GetConjunctsFromOriginalWhereClause()) { foreach (var cellConst in oneOfConst.Values.Values) { //if there is a mapping to this type... TypeConstant typeConst = cellConst as TypeConstant; if (typeConst != null) { unmapepdTypesInExtent.Remove(typeConst.CdmType); } } } } } //We are left with a type that has no mapping if (unmapepdTypesInExtent.Count > 0) { //error unmapped type m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternMissingMappingError, Strings.ViewGen_Missing_Type_Mapping_0(BuildCommaSeparatedErrorString (unmapepdTypesInExtent)), m_normalizer.AllWrappersForExtent, "")); } } } /// /// Finds errors related to splitting Conditions /// 1. Condition value is repeated across multiple types /// 2. A Column/attribute is mapped but also used as a condition /// private void MatchConditionErrors() { ListleftCellWrappers = m_normalizer.AllWrappersForExtent; //Stores violating Discriminator (condition member) so that we dont repeat the same error Set mappedConditionMembers = new Set (); //Both of these data-structs help in finding duplicate conditions Set setOfconditions = new Set (new ConditionComparer()); Dictionary firstLCWForCondition = new Dictionary (new ConditionComparer()); foreach (var leftCellWrapper in leftCellWrappers) { CompositeCondition condMembersValues = new CompositeCondition(); CellQuery cellQuery = leftCellWrapper.OnlyInputCell.GetLeftQuery(m_normalizer.SchemaContext.ViewTarget); foreach (OneOfConst condition in cellQuery.GetConjunctsFromWhereClause()) { MemberPath memberPath = condition.Slot.MemberPath; if (!m_domainMap.IsConditionMember(memberPath)) { continue; } OneOfScalarConst scalarCond = condition as OneOfScalarConst; //Check for mapping of Scalar member condition, ignore type conditions if (scalarCond != null && !mappedConditionMembers.Contains(memberPath) && /* prevents duplicate errors */ !(scalarCond.Values.Contains(CellConstant.NotNull) || scalarCond.Values.Contains(CellConstant.Null)) && /*mapping is allowed for NOT_NULL condition. We loosen the check and allow Null condition because if there is a null condition here, another mapping fragment may have not_null, in which case there is no error */ !leftCellWrapper.OnlyInputCell.CQuery.WhereClause.Equals(leftCellWrapper.OnlyInputCell.SQuery.WhereClause) /*allowed when both conditiosn are equal*/) { CheckConditionMemberIsNotMapped(memberPath, leftCellWrappers, mappedConditionMembers); } //CheckForDuplicateConditionValue //discover a composite condition of the form {path1=x, path2=y, ...} foreach (var element in condition.Values.Values) { Set values; //if not in the dict, add it if (!condMembersValues.TryGetValue(memberPath, out values)) { values = new Set (CellConstant.EqualityComparer); condMembersValues.Add(memberPath, values); } values.Add(element); } } //foreach condition if (condMembersValues.Count > 0) //it is possible that there are no condition members { //Check if the composite condition has been encountered before if (setOfconditions.Contains(condMembersValues)) { //Extents may be Equal on right side (e.g: by some form of Refconstraint) if (!RightSideEqual(firstLCWForCondition[condMembersValues], leftCellWrapper)) { //error duplicate conditions m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError, Strings.Viewgen_ErrorPattern_DuplicateConditionValue( BuildCommaSeparatedErrorString (condMembersValues.Keys) ), ToIEnum(firstLCWForCondition[condMembersValues].OnlyInputCell, leftCellWrapper.OnlyInputCell), "")); } } else { setOfconditions.Add(condMembersValues); //Remember which cell the condition came from.. used for error reporting firstLCWForCondition.Add(condMembersValues, leftCellWrapper); } } } //foreach fragment related to the Extent we are working on } /// /// When we are dealing with an update view, this method /// finds out if the given Table is mapped to different EntitySets /// private void MatchSplitErrors() { ListleftCellWrappers = m_normalizer.AllWrappersForExtent; //Check that the given Table is mapped to only one EntitySet (avoid AssociationSets) var nonAssociationWrappers = leftCellWrappers.Where(r => !(r.LeftExtent is AssociationSet) && !(r.RightCellQuery.Extent is AssociationSet)); if (m_normalizer.SchemaContext.ViewTarget == ViewTarget.UpdateView && nonAssociationWrappers.Any()) { LeftCellWrapper firstLeftCWrapper = nonAssociationWrappers.First(); EntitySetBase rightExtent = firstLeftCWrapper.RightCellQuery.Extent; foreach (var leftCellWrapper in nonAssociationWrappers) { //!(leftCellWrapper.RightCellQuery.Extent is AssociationSet) && if (!leftCellWrapper.RightCellQuery.Extent.EdmEquals(rightExtent)) { //A Table may be mapped to two extents but the extents may be Equal (by some form of Refconstraint) if(!RightSideEqual(leftCellWrapper, firstLeftCWrapper)) { //Report Error m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternSplittingError, Strings.Viewgen_ErrorPattern_TableMappedToMultipleES(leftCellWrapper.LeftExtent.ToString(), leftCellWrapper.RightCellQuery.Extent.ToString(), rightExtent.ToString()), leftCellWrapper.Cells.First(), "")); } } } } } /// /// Finds out whether fragments (partitions) violate constraints that would produce an invalid mapping. /// We compare equality/disjointness/containment for all 2-combinations of fragments. /// Error is reported if given relationship on S side is not maintained on the C side. /// If we know nothing about S-side then any relationship on C side is valid. /// private void MatchPartitionErrors() { ListmappingFragments = m_normalizer.AllWrappersForExtent; //for every 2-combination nC2 (n choose 2) int i = 0; foreach (var fragment1 in mappingFragments) { foreach (var fragment2 in mappingFragments.Skip(++i)) { FragmentQuery rightFragmentQuery1 = CreateRightFragmentQuery(fragment1); FragmentQuery rightFragmentQuery2 = CreateRightFragmentQuery(fragment2); bool isSDisjoint = CompareS(ComparisonOP.IsDisjointFrom, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); bool isCDisjoint = CompareC(ComparisonOP.IsDisjointFrom, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); bool is1SubsetOf2_C; bool is2SubsetOf1_C; bool is1SubsetOf2_S; bool is2SubsetOf1_S; bool isSEqual; bool isCEqual; if (isSDisjoint) { if (isCDisjoint) { continue; } else { //Figure out more info for accurate message is1SubsetOf2_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); is2SubsetOf1_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1); isCEqual = is1SubsetOf2_C && is2SubsetOf1_C; StringBuilder errorString = new StringBuilder(); //error if (isCEqual) //equal { //MSG: These two fragments are disjoint on the S-side but equal on the C-side. // Ensure disjointness on C-side by mapping them to different types within the same EntitySet // or by mapping them to the same type but with a C-side discriminator. //TestCase (1) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Eq); } else if (is1SubsetOf2_C || is2SubsetOf1_C) { //Really overlap is not accurate term (should be contianed in or subset of), but its easiest to read. if (CSideHasDifferentEntitySets(fragment1, fragment2)) { //MSG: These two fragments are disjoint on the S-side but overlap on the C-side via a Referential constraint. // Ensure disjointness on C-side by mapping them to different types within the same EntitySet // or by mapping them to the same type but with a C-side discriminator. //TestCase (Not possible because all PKs must be mapped) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Subs_Ref); } else { //MSG: These two fragments are disjoint on the S-side but overlap on the C-side. // Ensure disjointness on C-side. You may be using IsTypeOf() quantifier to // map multiple types within one of these fragments. //TestCase (2) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Subs); } } else //relationship is unknown { //MSG: These two fragments are disjoint on the S-side but not so on the C-side. // Ensure disjointness on C-side by mapping them to different types within the same EntitySet // or by mapping them to the same type but with a C-side discriminator. //TestCase (4) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Unk); } m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), "")); if (FoundTooManyErrors()) { return; } } } else { is1SubsetOf2_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); is2SubsetOf1_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1); } is1SubsetOf2_S = CompareS(ComparisonOP.IsContainedIn, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); is2SubsetOf1_S = CompareS(ComparisonOP.IsContainedIn, m_normalizer, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1); isCEqual = is1SubsetOf2_C && is2SubsetOf1_C; isSEqual = is1SubsetOf2_S && is2SubsetOf1_S; if (isSEqual) { if (isCEqual) //c-side equal { continue; } else { //error StringBuilder errorString = new StringBuilder(); if (isCDisjoint) { //MSG: These two fragments are equal on the S-side but disjoint on the C-side. // Either partition the S-side by adding a condition or remove any C-side conditions along with resulting redundant mapping fragments. // You may also map these two disjoint C-side partitions to different tables. //TestCase (5) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Disj); } else if (is1SubsetOf2_C || is2SubsetOf1_C) { if (CSideHasDifferentEntitySets(fragment1, fragment2)) { //MSG: These two fragments are equal on the S-side but overlap on the C-side. // It is likely that you have not added Referential Integrity constriaint for all Key attributes of both EntitySets. // Doing so would ensure equality on the C-side. //TestCase (Not possible, right?) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Subs_Ref); } else { //MSG: These two fragments are equal on the S-side but overlap on the C-side. // If you are using IsTypeOf() quantifier ensure both mapping fragments capture same types on the C-side. // Otherwise you may have intended to partition the S-side. //TestCase (6) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Subs); } } else //unknown { //S-side equal, C-side Unknown if (!IsQueryView() && (fragment1.OnlyInputCell.CQuery.Extent is AssociationSet || fragment2.OnlyInputCell.CQuery.Extent is AssociationSet)) { //one side is an association set errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Unk_----); } else { //MSG: These two fragments are equal on the S-side but not so on the C-side. // Try adding an Association with Referntial Integrity constraint if they are // mapped to different EntitySets in order to make theme equal on the C-side. //TestCase (no need, Table mapped to multiple ES tests cover this scenario) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Unk); } } m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), "")); if (FoundTooManyErrors()) { return; } } } else if (is1SubsetOf2_S || is2SubsetOf1_S) //proper subset - note: else if ensures inverse need not be checked { //C-side proper subset (c side must not be equal) if ((is1SubsetOf2_S && is1SubsetOf2_C == true && !(is2SubsetOf1_C==true)) || (is2SubsetOf1_S && is2SubsetOf1_C==true && !(is1SubsetOf2_C==true))) { continue; } else { //error StringBuilder errorString = new StringBuilder(); if (isCDisjoint) { //MSG: One of the fragments is a subset of the other on the S-side but they are disjoint on the C-side. // If you intended overlap on the S-side ensure they have similar relationship on teh C-side. // You may need to use IsTypeOf() quantifier or loosen conditions in one of the fragments. //TestCase (9, 10) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Disj); } else if (isCEqual) //equal { //MSG: One of the fragments is a subset of the other on the S-side but they are equal on the C-side. // If you intended overlap on the S-side ensure they have similar relationship on teh C-side. //TestCase (10) if (CSideHasDifferentEntitySets(fragment1, fragment2)) { // If they are equal via a Referential integrity constraint try making one a subset of the other by // not including all primary keys in the constraint. //TestCase (Not possible) errorString.Append(" "+Strings.Viewgen_ErrorPattern_Partition_Sub_Eq_Ref); } else { // You may need to modify conditions in one of the fragments. //TestCase (10) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Eq); } } else { //unknown //MSG: One of the fragments is a subset of the other on the S-side but they are disjoint on the C-side. // If you intended overlap on the S-side ensure they have similar relationship on teh C-side. //TestCase (no need, Table mapped to multiple ES tests cover this scenario) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Unk); } m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), "")); if (FoundTooManyErrors()) { return; } } } //else unknown relationship on the S-side } } //end looping over every 2-combination of fragment } private void CheckConditionMemberIsNotMapped(MemberPath conditionMember, List mappingFragments, Set mappedConditionMembers) { //Make sure memberPath is not mapped (in any other cells) foreach (var anotherFragment in mappingFragments) { foreach (var anotherCell in anotherFragment.Cells) { CellQuery anotherCellQuery = anotherCell.GetLeftQuery(m_normalizer.SchemaContext.ViewTarget); if (anotherCellQuery.GetProjectedMembers().Contains(conditionMember)) { mappedConditionMembers.Add(conditionMember); //error condition memer is projected somewhere m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError, Strings.Viewgen_ErrorPattern_ConditionMemberIsMapped(conditionMember.ToString()), anotherCell, "")); } } } } #endregion private bool FoundTooManyErrors() { return (m_errorLog.Count > m_originalErrorCount + NUM_PARTITION_ERR_TO_FIND); } #region Private Helpers private string BuildCommaSeparatedErrorString (IEnumerable members) { StringBuilder builder = new StringBuilder(); var firstMember = members.First(); foreach (var member in members) { if (!member.Equals(firstMember)) { builder.Append(", "); } builder.Append("'" + member.ToString() + "'"); } return builder.ToString(); } private bool CSideHasDifferentEntitySets(LeftCellWrapper a, LeftCellWrapper b) { if (IsQueryView()) { return a.LeftExtent == b.LeftExtent; } else { return a.RightCellQuery == b.RightCellQuery; } } private bool CompareC(ComparisonOP op, CellNormalizer normalizer, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2) { return Compare(true /*lookingForCSide*/, op, normalizer, leftWrapper1, leftWrapper2, rightQuery1, rightQuery2); } private bool CompareS(ComparisonOP op, CellNormalizer normalizer, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2) { return Compare(false/*lookingForCSide*/, op, normalizer, leftWrapper1, leftWrapper2, rightQuery1, rightQuery2); } private bool Compare(bool lookingForC, ComparisonOP op, CellNormalizer normalizer, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2) { LCWComparer comparer; if ((lookingForC && IsQueryView()) || (!lookingForC && !IsQueryView())) { if (op == ComparisonOP.IsContainedIn) { comparer = normalizer.LeftFragmentQP.IsContainedIn; } else if (op == ComparisonOP.IsDisjointFrom) { comparer = normalizer.LeftFragmentQP.IsDisjointFrom; } else { Debug.Fail("Unexpected comparison operator, only IsDisjointFrom and IsContainedIn are expected"); return false; } return comparer(leftWrapper1.FragmentQuery, leftWrapper2.FragmentQuery); } else { if (op == ComparisonOP.IsContainedIn) { comparer = normalizer.RightFragmentQP.IsContainedIn; } else if (op == ComparisonOP.IsDisjointFrom) { comparer = normalizer.RightFragmentQP.IsDisjointFrom; } else { Debug.Fail("Unexpected comparison operator, only IsDisjointFrom and IsContainedIn are expected"); return false; } return comparer(rightQuery1, rightQuery2); } } private bool RightSideEqual(LeftCellWrapper wrapper1, LeftCellWrapper wrapper2) { FragmentQuery rightFragmentQuery1 = CreateRightFragmentQuery(wrapper1); FragmentQuery rightFragmentQuery2 = CreateRightFragmentQuery(wrapper2); return m_normalizer.RightFragmentQP.IsEquivalentTo(rightFragmentQuery1, rightFragmentQuery2); } private FragmentQuery CreateRightFragmentQuery(LeftCellWrapper wrapper) { return FragmentQuery.Create(wrapper.OnlyInputCell.CellLabel.ToString(), wrapper.CreateRoleBoolean(), wrapper.OnlyInputCell.GetRightQuery(m_normalizer.SchemaContext.ViewTarget)); } private IEnumerable ToIEnum(Cell one, Cell two) { List | cells = new List | (); cells.Add(one); cells.Add(two); return cells; } private bool IsQueryView() { return (m_normalizer.SchemaContext.ViewTarget == ViewTarget.QueryView); } #endregion enum ComparisonOP { IsContainedIn, IsDisjointFrom } } class ConditionComparer : IEqualityComparer | >> { public bool Equals(Dictionary > one, Dictionary > two) { Set keysOfOne = new Set (one.Keys, MemberPath.EqualityComparer); Set keysOfTwo = new Set (two.Keys, MemberPath.EqualityComparer); if (!keysOfOne.SetEquals(keysOfTwo)) { return false; } foreach (var member in keysOfOne) { Set constantsOfOne = one[member]; Set constantsOfTwo = two[member]; if (!constantsOfOne.SetEquals(constantsOfTwo)) { return false; } } return true; } public int GetHashCode(Dictionary > obj) { return obj.ToString().ToLower(CultureInfo.InvariantCulture).GetHashCode(); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Common.Utils; using System.Collections.Generic; using System.Data.Mapping.ViewGeneration.Validation; using System.Data.Mapping.ViewGeneration.Structures; using System.Text; using System.Diagnostics; using System.Collections.ObjectModel; using System.Data.Mapping.ViewGeneration.Utils; using System.Data.Metadata.Edm; using System.Data.Entity; using System.Data.Common.Utils.Boolean; using System.Linq; using System.Data.Mapping.ViewGeneration.QueryRewriting; namespace System.Data.Mapping.ViewGeneration.Validation { using CompositeCondition = Dictionary>; using System.Globalization; delegate bool LCWComparer(FragmentQuery query1, FragmentQuery query2); internal class ErrorPatternMatcher { private CellNormalizer m_normalizer; private MemberDomainMap m_domainMap; private IEnumerable m_keyAttributes; private ErrorLog m_errorLog; private int m_originalErrorCount; private const int NUM_PARTITION_ERR_TO_FIND = 5; #region Constructor private ErrorPatternMatcher(CellNormalizer normalizer, MemberDomainMap domainMap, ErrorLog errorLog) { m_normalizer = normalizer; m_domainMap = domainMap; m_keyAttributes = MemberPath.GetKeyMembers(normalizer.Extent, domainMap, normalizer.Workspace); m_errorLog = errorLog; m_originalErrorCount = m_errorLog.Count; } public static bool FindMappingErrors(CellNormalizer normalizer, MemberDomainMap domainMap, ErrorLog errorLog) { ErrorPatternMatcher matcher = new ErrorPatternMatcher(normalizer, domainMap, errorLog); matcher.MatchMissingMappingErrors(); matcher.MatchConditionErrors(); matcher.MatchSplitErrors(); if (matcher.m_errorLog.Count == matcher.m_originalErrorCount) { //this will generate redundant errors if one of the above routine finds an error // so execute it only when we dont have any other errors matcher.MatchPartitionErrors(); } if (matcher.m_errorLog.Count > matcher.m_originalErrorCount) { ExceptionHelpers.ThrowMappingException(matcher.m_errorLog, matcher.m_normalizer.Config); } return false; } #endregion #region Error Matching Routines /// /// Finds Types (possibly without any members) that have no mapping specified /// private void MatchMissingMappingErrors() { if (m_normalizer.SchemaContext.ViewTarget == ViewTarget.QueryView) { //Find all types for the given EntitySet SetunmapepdTypesInExtent = new Set (MetadataHelper.GetTypeAndSubtypesOf(m_normalizer.Extent.ElementType, m_normalizer.Workspace, false /*isAbstract*/)); //Figure out which type has no Cell mapped to it foreach (var fragment in m_normalizer.AllWrappersForExtent) { foreach (Cell cell in fragment.Cells) { foreach( var oneOfConst in cell.CQuery.GetConjunctsFromOriginalWhereClause()) { foreach (var cellConst in oneOfConst.Values.Values) { //if there is a mapping to this type... TypeConstant typeConst = cellConst as TypeConstant; if (typeConst != null) { unmapepdTypesInExtent.Remove(typeConst.CdmType); } } } } } //We are left with a type that has no mapping if (unmapepdTypesInExtent.Count > 0) { //error unmapped type m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternMissingMappingError, Strings.ViewGen_Missing_Type_Mapping_0(BuildCommaSeparatedErrorString (unmapepdTypesInExtent)), m_normalizer.AllWrappersForExtent, "")); } } } /// /// Finds errors related to splitting Conditions /// 1. Condition value is repeated across multiple types /// 2. A Column/attribute is mapped but also used as a condition /// private void MatchConditionErrors() { ListleftCellWrappers = m_normalizer.AllWrappersForExtent; //Stores violating Discriminator (condition member) so that we dont repeat the same error Set mappedConditionMembers = new Set (); //Both of these data-structs help in finding duplicate conditions Set setOfconditions = new Set (new ConditionComparer()); Dictionary firstLCWForCondition = new Dictionary (new ConditionComparer()); foreach (var leftCellWrapper in leftCellWrappers) { CompositeCondition condMembersValues = new CompositeCondition(); CellQuery cellQuery = leftCellWrapper.OnlyInputCell.GetLeftQuery(m_normalizer.SchemaContext.ViewTarget); foreach (OneOfConst condition in cellQuery.GetConjunctsFromWhereClause()) { MemberPath memberPath = condition.Slot.MemberPath; if (!m_domainMap.IsConditionMember(memberPath)) { continue; } OneOfScalarConst scalarCond = condition as OneOfScalarConst; //Check for mapping of Scalar member condition, ignore type conditions if (scalarCond != null && !mappedConditionMembers.Contains(memberPath) && /* prevents duplicate errors */ !(scalarCond.Values.Contains(CellConstant.NotNull) || scalarCond.Values.Contains(CellConstant.Null)) && /*mapping is allowed for NOT_NULL condition. We loosen the check and allow Null condition because if there is a null condition here, another mapping fragment may have not_null, in which case there is no error */ !leftCellWrapper.OnlyInputCell.CQuery.WhereClause.Equals(leftCellWrapper.OnlyInputCell.SQuery.WhereClause) /*allowed when both conditiosn are equal*/) { CheckConditionMemberIsNotMapped(memberPath, leftCellWrappers, mappedConditionMembers); } //CheckForDuplicateConditionValue //discover a composite condition of the form {path1=x, path2=y, ...} foreach (var element in condition.Values.Values) { Set values; //if not in the dict, add it if (!condMembersValues.TryGetValue(memberPath, out values)) { values = new Set (CellConstant.EqualityComparer); condMembersValues.Add(memberPath, values); } values.Add(element); } } //foreach condition if (condMembersValues.Count > 0) //it is possible that there are no condition members { //Check if the composite condition has been encountered before if (setOfconditions.Contains(condMembersValues)) { //Extents may be Equal on right side (e.g: by some form of Refconstraint) if (!RightSideEqual(firstLCWForCondition[condMembersValues], leftCellWrapper)) { //error duplicate conditions m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError, Strings.Viewgen_ErrorPattern_DuplicateConditionValue( BuildCommaSeparatedErrorString (condMembersValues.Keys) ), ToIEnum(firstLCWForCondition[condMembersValues].OnlyInputCell, leftCellWrapper.OnlyInputCell), "")); } } else { setOfconditions.Add(condMembersValues); //Remember which cell the condition came from.. used for error reporting firstLCWForCondition.Add(condMembersValues, leftCellWrapper); } } } //foreach fragment related to the Extent we are working on } /// /// When we are dealing with an update view, this method /// finds out if the given Table is mapped to different EntitySets /// private void MatchSplitErrors() { ListleftCellWrappers = m_normalizer.AllWrappersForExtent; //Check that the given Table is mapped to only one EntitySet (avoid AssociationSets) var nonAssociationWrappers = leftCellWrappers.Where(r => !(r.LeftExtent is AssociationSet) && !(r.RightCellQuery.Extent is AssociationSet)); if (m_normalizer.SchemaContext.ViewTarget == ViewTarget.UpdateView && nonAssociationWrappers.Any()) { LeftCellWrapper firstLeftCWrapper = nonAssociationWrappers.First(); EntitySetBase rightExtent = firstLeftCWrapper.RightCellQuery.Extent; foreach (var leftCellWrapper in nonAssociationWrappers) { //!(leftCellWrapper.RightCellQuery.Extent is AssociationSet) && if (!leftCellWrapper.RightCellQuery.Extent.EdmEquals(rightExtent)) { //A Table may be mapped to two extents but the extents may be Equal (by some form of Refconstraint) if(!RightSideEqual(leftCellWrapper, firstLeftCWrapper)) { //Report Error m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternSplittingError, Strings.Viewgen_ErrorPattern_TableMappedToMultipleES(leftCellWrapper.LeftExtent.ToString(), leftCellWrapper.RightCellQuery.Extent.ToString(), rightExtent.ToString()), leftCellWrapper.Cells.First(), "")); } } } } } /// /// Finds out whether fragments (partitions) violate constraints that would produce an invalid mapping. /// We compare equality/disjointness/containment for all 2-combinations of fragments. /// Error is reported if given relationship on S side is not maintained on the C side. /// If we know nothing about S-side then any relationship on C side is valid. /// private void MatchPartitionErrors() { ListmappingFragments = m_normalizer.AllWrappersForExtent; //for every 2-combination nC2 (n choose 2) int i = 0; foreach (var fragment1 in mappingFragments) { foreach (var fragment2 in mappingFragments.Skip(++i)) { FragmentQuery rightFragmentQuery1 = CreateRightFragmentQuery(fragment1); FragmentQuery rightFragmentQuery2 = CreateRightFragmentQuery(fragment2); bool isSDisjoint = CompareS(ComparisonOP.IsDisjointFrom, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); bool isCDisjoint = CompareC(ComparisonOP.IsDisjointFrom, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); bool is1SubsetOf2_C; bool is2SubsetOf1_C; bool is1SubsetOf2_S; bool is2SubsetOf1_S; bool isSEqual; bool isCEqual; if (isSDisjoint) { if (isCDisjoint) { continue; } else { //Figure out more info for accurate message is1SubsetOf2_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); is2SubsetOf1_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1); isCEqual = is1SubsetOf2_C && is2SubsetOf1_C; StringBuilder errorString = new StringBuilder(); //error if (isCEqual) //equal { //MSG: These two fragments are disjoint on the S-side but equal on the C-side. // Ensure disjointness on C-side by mapping them to different types within the same EntitySet // or by mapping them to the same type but with a C-side discriminator. //TestCase (1) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Eq); } else if (is1SubsetOf2_C || is2SubsetOf1_C) { //Really overlap is not accurate term (should be contianed in or subset of), but its easiest to read. if (CSideHasDifferentEntitySets(fragment1, fragment2)) { //MSG: These two fragments are disjoint on the S-side but overlap on the C-side via a Referential constraint. // Ensure disjointness on C-side by mapping them to different types within the same EntitySet // or by mapping them to the same type but with a C-side discriminator. //TestCase (Not possible because all PKs must be mapped) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Subs_Ref); } else { //MSG: These two fragments are disjoint on the S-side but overlap on the C-side. // Ensure disjointness on C-side. You may be using IsTypeOf() quantifier to // map multiple types within one of these fragments. //TestCase (2) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Subs); } } else //relationship is unknown { //MSG: These two fragments are disjoint on the S-side but not so on the C-side. // Ensure disjointness on C-side by mapping them to different types within the same EntitySet // or by mapping them to the same type but with a C-side discriminator. //TestCase (4) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Disj_Unk); } m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), "")); if (FoundTooManyErrors()) { return; } } } else { is1SubsetOf2_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); is2SubsetOf1_C = CompareC(ComparisonOP.IsContainedIn, m_normalizer, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1); } is1SubsetOf2_S = CompareS(ComparisonOP.IsContainedIn, m_normalizer, fragment1, fragment2, rightFragmentQuery1, rightFragmentQuery2); is2SubsetOf1_S = CompareS(ComparisonOP.IsContainedIn, m_normalizer, fragment2, fragment1, rightFragmentQuery2, rightFragmentQuery1); isCEqual = is1SubsetOf2_C && is2SubsetOf1_C; isSEqual = is1SubsetOf2_S && is2SubsetOf1_S; if (isSEqual) { if (isCEqual) //c-side equal { continue; } else { //error StringBuilder errorString = new StringBuilder(); if (isCDisjoint) { //MSG: These two fragments are equal on the S-side but disjoint on the C-side. // Either partition the S-side by adding a condition or remove any C-side conditions along with resulting redundant mapping fragments. // You may also map these two disjoint C-side partitions to different tables. //TestCase (5) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Disj); } else if (is1SubsetOf2_C || is2SubsetOf1_C) { if (CSideHasDifferentEntitySets(fragment1, fragment2)) { //MSG: These two fragments are equal on the S-side but overlap on the C-side. // It is likely that you have not added Referential Integrity constriaint for all Key attributes of both EntitySets. // Doing so would ensure equality on the C-side. //TestCase (Not possible, right?) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Subs_Ref); } else { //MSG: These two fragments are equal on the S-side but overlap on the C-side. // If you are using IsTypeOf() quantifier ensure both mapping fragments capture same types on the C-side. // Otherwise you may have intended to partition the S-side. //TestCase (6) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Subs); } } else //unknown { //S-side equal, C-side Unknown if (!IsQueryView() && (fragment1.OnlyInputCell.CQuery.Extent is AssociationSet || fragment2.OnlyInputCell.CQuery.Extent is AssociationSet)) { //one side is an association set errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Unk_----); } else { //MSG: These two fragments are equal on the S-side but not so on the C-side. // Try adding an Association with Referntial Integrity constraint if they are // mapped to different EntitySets in order to make theme equal on the C-side. //TestCase (no need, Table mapped to multiple ES tests cover this scenario) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Eq_Unk); } } m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), "")); if (FoundTooManyErrors()) { return; } } } else if (is1SubsetOf2_S || is2SubsetOf1_S) //proper subset - note: else if ensures inverse need not be checked { //C-side proper subset (c side must not be equal) if ((is1SubsetOf2_S && is1SubsetOf2_C == true && !(is2SubsetOf1_C==true)) || (is2SubsetOf1_S && is2SubsetOf1_C==true && !(is1SubsetOf2_C==true))) { continue; } else { //error StringBuilder errorString = new StringBuilder(); if (isCDisjoint) { //MSG: One of the fragments is a subset of the other on the S-side but they are disjoint on the C-side. // If you intended overlap on the S-side ensure they have similar relationship on teh C-side. // You may need to use IsTypeOf() quantifier or loosen conditions in one of the fragments. //TestCase (9, 10) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Disj); } else if (isCEqual) //equal { //MSG: One of the fragments is a subset of the other on the S-side but they are equal on the C-side. // If you intended overlap on the S-side ensure they have similar relationship on teh C-side. //TestCase (10) if (CSideHasDifferentEntitySets(fragment1, fragment2)) { // If they are equal via a Referential integrity constraint try making one a subset of the other by // not including all primary keys in the constraint. //TestCase (Not possible) errorString.Append(" "+Strings.Viewgen_ErrorPattern_Partition_Sub_Eq_Ref); } else { // You may need to modify conditions in one of the fragments. //TestCase (10) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Eq); } } else { //unknown //MSG: One of the fragments is a subset of the other on the S-side but they are disjoint on the C-side. // If you intended overlap on the S-side ensure they have similar relationship on teh C-side. //TestCase (no need, Table mapped to multiple ES tests cover this scenario) errorString.Append(Strings.Viewgen_ErrorPattern_Partition_Sub_Unk); } m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternInvalidPartitionError, errorString.ToString(), ToIEnum(fragment1.OnlyInputCell, fragment2.OnlyInputCell), "")); if (FoundTooManyErrors()) { return; } } } //else unknown relationship on the S-side } } //end looping over every 2-combination of fragment } private void CheckConditionMemberIsNotMapped(MemberPath conditionMember, List mappingFragments, Set mappedConditionMembers) { //Make sure memberPath is not mapped (in any other cells) foreach (var anotherFragment in mappingFragments) { foreach (var anotherCell in anotherFragment.Cells) { CellQuery anotherCellQuery = anotherCell.GetLeftQuery(m_normalizer.SchemaContext.ViewTarget); if (anotherCellQuery.GetProjectedMembers().Contains(conditionMember)) { mappedConditionMembers.Add(conditionMember); //error condition memer is projected somewhere m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.ErrorPatternConditionError, Strings.Viewgen_ErrorPattern_ConditionMemberIsMapped(conditionMember.ToString()), anotherCell, "")); } } } } #endregion private bool FoundTooManyErrors() { return (m_errorLog.Count > m_originalErrorCount + NUM_PARTITION_ERR_TO_FIND); } #region Private Helpers private string BuildCommaSeparatedErrorString (IEnumerable members) { StringBuilder builder = new StringBuilder(); var firstMember = members.First(); foreach (var member in members) { if (!member.Equals(firstMember)) { builder.Append(", "); } builder.Append("'" + member.ToString() + "'"); } return builder.ToString(); } private bool CSideHasDifferentEntitySets(LeftCellWrapper a, LeftCellWrapper b) { if (IsQueryView()) { return a.LeftExtent == b.LeftExtent; } else { return a.RightCellQuery == b.RightCellQuery; } } private bool CompareC(ComparisonOP op, CellNormalizer normalizer, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2) { return Compare(true /*lookingForCSide*/, op, normalizer, leftWrapper1, leftWrapper2, rightQuery1, rightQuery2); } private bool CompareS(ComparisonOP op, CellNormalizer normalizer, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2) { return Compare(false/*lookingForCSide*/, op, normalizer, leftWrapper1, leftWrapper2, rightQuery1, rightQuery2); } private bool Compare(bool lookingForC, ComparisonOP op, CellNormalizer normalizer, LeftCellWrapper leftWrapper1, LeftCellWrapper leftWrapper2, FragmentQuery rightQuery1, FragmentQuery rightQuery2) { LCWComparer comparer; if ((lookingForC && IsQueryView()) || (!lookingForC && !IsQueryView())) { if (op == ComparisonOP.IsContainedIn) { comparer = normalizer.LeftFragmentQP.IsContainedIn; } else if (op == ComparisonOP.IsDisjointFrom) { comparer = normalizer.LeftFragmentQP.IsDisjointFrom; } else { Debug.Fail("Unexpected comparison operator, only IsDisjointFrom and IsContainedIn are expected"); return false; } return comparer(leftWrapper1.FragmentQuery, leftWrapper2.FragmentQuery); } else { if (op == ComparisonOP.IsContainedIn) { comparer = normalizer.RightFragmentQP.IsContainedIn; } else if (op == ComparisonOP.IsDisjointFrom) { comparer = normalizer.RightFragmentQP.IsDisjointFrom; } else { Debug.Fail("Unexpected comparison operator, only IsDisjointFrom and IsContainedIn are expected"); return false; } return comparer(rightQuery1, rightQuery2); } } private bool RightSideEqual(LeftCellWrapper wrapper1, LeftCellWrapper wrapper2) { FragmentQuery rightFragmentQuery1 = CreateRightFragmentQuery(wrapper1); FragmentQuery rightFragmentQuery2 = CreateRightFragmentQuery(wrapper2); return m_normalizer.RightFragmentQP.IsEquivalentTo(rightFragmentQuery1, rightFragmentQuery2); } private FragmentQuery CreateRightFragmentQuery(LeftCellWrapper wrapper) { return FragmentQuery.Create(wrapper.OnlyInputCell.CellLabel.ToString(), wrapper.CreateRoleBoolean(), wrapper.OnlyInputCell.GetRightQuery(m_normalizer.SchemaContext.ViewTarget)); } private IEnumerable ToIEnum(Cell one, Cell two) { List | cells = new List | (); cells.Add(one); cells.Add(two); return cells; } private bool IsQueryView() { return (m_normalizer.SchemaContext.ViewTarget == ViewTarget.QueryView); } #endregion enum ComparisonOP { IsContainedIn, IsDisjointFrom } } class ConditionComparer : IEqualityComparer | >> { public bool Equals(Dictionary > one, Dictionary > two) { Set keysOfOne = new Set (one.Keys, MemberPath.EqualityComparer); Set keysOfTwo = new Set (two.Keys, MemberPath.EqualityComparer); if (!keysOfOne.SetEquals(keysOfTwo)) { return false; } foreach (var member in keysOfOne) { Set constantsOfOne = one[member]; Set constantsOfTwo = two[member]; if (!constantsOfOne.SetEquals(constantsOfTwo)) { return false; } } return true; } public int GetHashCode(Dictionary > obj) { return obj.ToString().ToLower(CultureInfo.InvariantCulture).GetHashCode(); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlEnumAttribute.cs
- BufferBuilder.cs
- Stroke2.cs
- MemoryStream.cs
- Freezable.cs
- CriticalFinalizerObject.cs
- TextEditorSpelling.cs
- Normalization.cs
- AutoResetEvent.cs
- Compiler.cs
- RtfToXamlReader.cs
- D3DImage.cs
- ServicesSection.cs
- EntityCollection.cs
- Model3D.cs
- ComponentDispatcher.cs
- XpsFixedDocumentSequenceReaderWriter.cs
- ScrollChrome.cs
- AsnEncodedData.cs
- Debugger.cs
- KerberosSecurityTokenAuthenticator.cs
- VirtualPathProvider.cs
- UpdatePanel.cs
- DebugManager.cs
- MessageAction.cs
- DCSafeHandle.cs
- VirtualDirectoryMapping.cs
- ReadOnlyAttribute.cs
- PropertyPushdownHelper.cs
- CriticalExceptions.cs
- UiaCoreApi.cs
- XsltArgumentList.cs
- ModelPropertyImpl.cs
- IItemProperties.cs
- ReadOnlyKeyedCollection.cs
- SymbolType.cs
- ConsoleKeyInfo.cs
- EventLogPermissionEntryCollection.cs
- XmlSchemaChoice.cs
- EnumBuilder.cs
- XslCompiledTransform.cs
- ResXResourceSet.cs
- ObjectStateFormatter.cs
- ReadWriteSpinLock.cs
- RequestQueryParser.cs
- ResourceDictionary.cs
- XomlDesignerLoader.cs
- FolderLevelBuildProviderAppliesToAttribute.cs
- EncoderNLS.cs
- ManifestResourceInfo.cs
- ComplexObject.cs
- CorrelationInitializer.cs
- ImageClickEventArgs.cs
- Registry.cs
- SHA512Cng.cs
- PropertyDescriptorCollection.cs
- login.cs
- HttpResponseWrapper.cs
- ComponentEvent.cs
- SmtpFailedRecipientsException.cs
- DecoderFallback.cs
- XmlDataLoader.cs
- DiscoveryDocumentLinksPattern.cs
- FilterException.cs
- UnmanagedMarshal.cs
- HtmlControl.cs
- DataGridLinkButton.cs
- XpsTokenContext.cs
- BadImageFormatException.cs
- PasswordBoxAutomationPeer.cs
- HttpWebRequest.cs
- SortedDictionary.cs
- DataGridViewCell.cs
- ComponentCollection.cs
- _ConnectStream.cs
- TextBoxAutomationPeer.cs
- SoapHeaderAttribute.cs
- SqlUDTStorage.cs
- DynamicDataRoute.cs
- StickyNote.cs
- objectquery_tresulttype.cs
- SerialErrors.cs
- SqlCacheDependencySection.cs
- CodeCatchClauseCollection.cs
- LockRecursionException.cs
- HwndSourceKeyboardInputSite.cs
- GorillaCodec.cs
- Model3DGroup.cs
- FileVersion.cs
- StorageBasedPackageProperties.cs
- AutoResetEvent.cs
- WindowsGraphicsCacheManager.cs
- DataGridViewHitTestInfo.cs
- SqlExpander.cs
- TableLayoutSettings.cs
- RenderDataDrawingContext.cs
- SamlSubject.cs
- ImplicitInputBrush.cs
- RIPEMD160.cs
- Encoder.cs