Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Validation / ForeignConstraint.cs / 1305376 / ForeignConstraint.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Common.Utils; using System.Data.Common.Utils.Boolean; using System.Data.Mapping.ViewGeneration.Structures; using System.Data.Mapping.ViewGeneration.Utils; using System.Data.Mapping.ViewGeneration.QueryRewriting; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Data.Metadata.Edm; using System.Linq; namespace System.Data.Mapping.ViewGeneration.Validation { using WrapperBoolExpr = BoolExpr; using WrapperOrExpr = OrExpr ; using WrapperNotExpr = NotExpr ; using WrapperTermExpr = TermExpr ; // An abstraction that captures a foreign key constraint: // --> internal class ForeignConstraint : InternalBase { #region Constructor // effects: Creates a foreign key constraint of the form: // --> // i_fkeySet is the name of the constraint internal ForeignConstraint(AssociationSet i_fkeySet, EntitySet i_parentTable, EntitySet i_childTable, ReadOnlyMetadataCollection i_parentColumns, ReadOnlyMetadataCollection i_childColumns) { m_fKeySet = i_fkeySet; m_parentTable = i_parentTable; m_childTable = i_childTable; m_childColumns = new List (); // Create parent and child paths using the table names foreach (EdmProperty property in i_childColumns) { MemberPath path = new MemberPath(m_childTable, property); m_childColumns.Add(path); } m_parentColumns = new List (); foreach (EdmProperty property in i_parentColumns) { MemberPath path = new MemberPath(m_parentTable, property); m_parentColumns.Add(path); } } #endregion #region Fields private AssociationSet m_fKeySet; // Just for debugging private EntitySet m_parentTable; private EntitySet m_childTable; private List m_parentColumns; private List m_childColumns; #endregion #region Properties internal EntitySet ParentTable { get { return m_parentTable; } } internal EntitySet ChildTable { get { return m_childTable; } } internal IEnumerable ChildColumns { get { return m_childColumns; } } internal IEnumerable ParentColumns { get { return m_parentColumns; } } #endregion #region Externally available Methods // effects: Given a store-side container, returns all the foreign key // constraints specified for different tables internal static List GetForeignConstraints(EntityContainer container) { List foreignKeyConstraints = new List (); // Go through all the extents and get the associations foreach (EntitySetBase extent in container.BaseEntitySets) { AssociationSet relationSet = extent as AssociationSet; if (relationSet == null) continue; // Keep track of the end to EntitySet mapping Dictionary endToExtents = new Dictionary (); foreach (AssociationSetEnd end in relationSet.AssociationSetEnds) { endToExtents.Add(end.Name, end.EntitySet); } AssociationType relationType = relationSet.ElementType; // Go through each referential constraint, determine the name // of the tables that the constraint refers to and then // create the foreign key constraint between the tables // Wow! We go to great lengths to make it cumbersome for a // programmer to deal with foreign keys foreach (ReferentialConstraint constraint in relationType.ReferentialConstraints) { // Note: We are correlating the constraint's roles with // the ends above using the role names, i.e., // FromRole.Name and ToRole.Name here and end.Role above EntitySet parentExtent = endToExtents[constraint.FromRole.Name]; EntitySet childExtent = endToExtents[constraint.ToRole.Name]; ForeignConstraint foreignKeyConstraint = new ForeignConstraint(relationSet, parentExtent, childExtent, constraint.FromProperties, constraint.ToProperties); foreignKeyConstraints.Add(foreignKeyConstraint); } } return foreignKeyConstraints; } // effects: Checks that this foreign key constraints for all the // tables are being ensured on the C-side as well. If not, adds // errors to the errorLog internal void CheckConstraint(Set cells, QueryRewriter childRewriter, QueryRewriter parentRewriter, ErrorLog errorLog, ConfigViewGenerator config) { if (IsConstraintRelevantForCells(cells) == false) { // if the constraint does not deal with any cell in this group, ignore it return; } if (config.IsNormalTracing) { Trace.WriteLine(String.Empty); Trace.WriteLine(String.Empty); Trace.Write("Checking: "); Trace.WriteLine(this); } if (childRewriter == null && parentRewriter == null) { // Neither table is mapped - so we are fine return; } // If the child table has not been mapped, we used to say that we // are fine. However, if we have SPerson(pid) and SAddress(aid, // pid), where pid is an FK into SPerson, we are in trouble if // SAddress is not mapped - SPerson could get deleted. So we // check for it as well // if the parent table is not mapped, we also have a problem if (childRewriter == null) { string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_Missing_Table_Mapping_1( ToUserString(), ChildTable.Name); // Get the cells from the parent table ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyMissingTableMapping, message, parentRewriter.UsedCells, String.Empty); errorLog.AddEntry(record); return; } if (parentRewriter == null) { string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_Missing_Table_Mapping_1( ToUserString(), ParentTable.Name); // Get the cells from the child table ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyMissingTableMapping, message, childRewriter.UsedCells, String.Empty); errorLog.AddEntry(record); return; } // Note: we do not check if the parent columns correspond to the // table's keys - metadata checks for that //First check if the FK is covered by Foreign Key Association //If we find this, we don't need to check for independent associations. If user maps the Fk to both FK and independent associations, //the regular round tripping validation will catch the error. if (CheckIfConstraintMappedToForeignKeyAssociation(childRewriter, parentRewriter, cells, errorLog)) { return; } // Check if the foreign key in the child table corresponds to the primary key, i.e., if // the foreign key (e.g., pid, pid2) is a superset of the actual key members (e.g., pid), it means // that the foreign key is also the primary key for this table -- so we can propagate the queries upto C-Space // rather than doing the cell check int initialErrorLogSize = errorLog.Count; if (IsForeignKeySuperSetOfPrimaryKeyInChildTable()) { GuaranteeForeignKeyConstraintInCSpace(childRewriter, parentRewriter, errorLog, config); } else { GuaranteeMappedRelationshipForForeignKey(childRewriter, parentRewriter, cells, errorLog, config); } if (initialErrorLogSize == errorLog.Count) { // Check if the order of columns in foreign key correponds to the // mappings in m_cellGroup, e.g., if | in SAddress is // a foreign key into of the SPerson table, make // sure that this order is preserved through the mappings in m_cellGroup CheckForeignKeyColumnOrder(cells, errorLog); } } #endregion #region Methods (mostly) for Query Containment Check via Keys // requires: constraint.ChildColumns form a key in // constraint.ChildTable (actually they should subsume the primary key) private void GuaranteeForeignKeyConstraintInCSpace(QueryRewriter childRewriter, QueryRewriter parentRewriter, ErrorLog errorLog, ConfigViewGenerator config) { ViewgenContext childContext = childRewriter.ViewgenContext; ViewgenContext parentContext = parentRewriter.ViewgenContext; CellTreeNode cNode = childRewriter.BasicView; CellTreeNode pNode = parentRewriter.BasicView; FragmentQueryProcessor qp = FragmentQueryProcessor.Merge(childContext.RightFragmentQP, parentContext.RightFragmentQP); bool cImpliesP = qp.IsContainedIn(cNode.RightFragmentQuery, pNode.RightFragmentQuery); if (false == cImpliesP) { // Foreign key constraint not being ensured in C-space string childExtents = LeftCellWrapper.GetExtentListAsUserString(cNode.GetLeaves()); string parentExtents = LeftCellWrapper.GetExtentListAsUserString(pNode.GetLeaves()); string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_Not_Guaranteed_InCSpace_0( ToUserString()); // Add all wrappers into allWrappers Set allWrappers = new Set (pNode.GetLeaves()); allWrappers.AddRange(cNode.GetLeaves()); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyNotGuaranteedInCSpace, message, allWrappers, String.Empty); errorLog.AddEntry(record); } } #endregion #region Methods for Foreign Keys mapped to association // effects: Ensures that there is a relationship mapped into the C-space for some cell in m_cellGroup. Else // adds an error to errorLog private void GuaranteeMappedRelationshipForForeignKey(QueryRewriter childRewriter, QueryRewriter parentRewriter, IEnumerable cells, ErrorLog errorLog, ConfigViewGenerator config) { ViewgenContext childContext = childRewriter.ViewgenContext; ViewgenContext parentContext = parentRewriter.ViewgenContext; // Find a cell where this foreign key is mapped as a relationship MemberPath prefix = new MemberPath(ChildTable); ExtentKey primaryKey = ExtentKey.GetPrimaryKeyForEntityType(prefix, ChildTable.ElementType); IEnumerable | primaryKeyFields = primaryKey.KeyFields; bool foundCell = false; bool foundValidParentColumnsForForeignKey = false; //we need to find only one, dont error on any one check being false List errorListForInvalidParentColumnsForForeignKey = null; foreach (Cell cell in cells) { if (cell.SQuery.Extent.Equals(ChildTable) == false) { continue; } // The childtable is mapped to a relationship in the C-space in cell // Check that all the columns of the foreign key and the primary key in the child table are mapped to some // property in the C-space AssociationEndMember parentEnd = GetRelationEndForColumns(cell, ChildColumns); if (parentEnd != null && CheckParentColumnsForForeignKey(cell, cells, parentEnd, ref errorListForInvalidParentColumnsForForeignKey) == false) { // Not an error unless we find no valid case continue; } else { foundValidParentColumnsForForeignKey = true; } AssociationEndMember childEnd = GetRelationEndForColumns(cell, primaryKeyFields); Debug.Assert(childEnd == null || parentEnd != childEnd, "Ends are same => PKey and child columns are same - code should gone to other method"); // Note: If both of them are not-null, they are mapped to the // same association set -- since we checked that particular cell if (childEnd != null && parentEnd != null && FindEntitySetForColumnsMappedToEntityKeys(cells, primaryKeyFields) != null) { foundCell = true; CheckConstraintWhenParentChildMapped(cell, errorLog, parentEnd, config); break; // Done processing for the foreign key - either it was mapped correctly or it was not } else if (parentEnd != null) { // At this point, we know cell corresponds to an association set AssociationSet assocSet = (AssociationSet)cell.CQuery.Extent; EntitySet parentSet = MetadataHelper.GetEntitySetAtEnd(assocSet, parentEnd); foundCell = CheckConstraintWhenOnlyParentMapped(cell, parentSet, assocSet, parentEnd, childRewriter, parentRewriter, config); if (foundCell) { break; } } } //CheckParentColumnsForForeignKey has returned no matches, Error. if (!foundValidParentColumnsForForeignKey) { Debug.Assert(errorListForInvalidParentColumnsForForeignKey != null && errorListForInvalidParentColumnsForForeignKey.Count > 0); foreach (var errorRecord in errorListForInvalidParentColumnsForForeignKey) { errorLog.AddEntry(errorRecord); } return; } if (foundCell == false) { // No cell found -- Declare error string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_Missing_Relationship_Mapping_0(ToUserString()); IEnumerable parentWrappers = GetWrappersFromContext(parentContext, ParentTable); IEnumerable childWrappers = GetWrappersFromContext(childContext, ChildTable); Set bothExtentWrappers = new Set (parentWrappers); bothExtentWrappers.AddRange(childWrappers); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyMissingRelationshipMapping, message, bothExtentWrappers, String.Empty); errorLog.AddEntry(record); } } private bool CheckIfConstraintMappedToForeignKeyAssociation(QueryRewriter childRewriter, QueryRewriter parentRewriter, Set cells, ErrorLog errorLog) { ViewgenContext childContext = childRewriter.ViewgenContext; ViewgenContext parentContext = parentRewriter.ViewgenContext; //First collect the sets of properties that the principal and dependant ends of this FK //are mapped to in the Edm side. var childPropertiesSet = new List | >(); var parentPropertiesSet = new List >(); foreach (Cell cell in cells) { if (cell.CQuery.Extent.BuiltInTypeKind != BuiltInTypeKind.AssociationSet) { var childProperties = cell.GetCSlotsForTableColumns(ChildColumns); if ( (childProperties != null) && (childProperties.Count != 0)) { childPropertiesSet.Add(childProperties); } var parentProperties = cell.GetCSlotsForTableColumns(ParentColumns); if ((parentProperties != null) && (parentProperties.Count != 0)) { parentPropertiesSet.Add(parentProperties); } } } //Now Check if the properties on the Edm side are connected via an FK relationship. if ((childPropertiesSet.Count != 0) && (parentPropertiesSet.Count != 0)) { var foreignKeyAssociations = childContext.EntityContainerMapping.EdmEntityContainer.BaseEntitySets.OfType ().Where(it => it.ElementType.IsForeignKey).Select(it => it.ElementType); foreach (AssociationType association in foreignKeyAssociations) { ReferentialConstraint refConstraint = association.ReferentialConstraints.FirstOrDefault(); //We need to check to see if the dependent properties that were mapped from S side are present as //dependant properties of this ref constraint on the Edm side. We need to do the same for principal side but //we can not enforce equality since the order of the properties participating in the constraint on the S side and //C side could be different. This is OK as long as they are mapped appropriately. We also can not use Existance as a sufficient //condition since it will allow invalid mapping where FK columns could have been flipped when mapping to the Edm side. So //we make sure that the index of the properties in the principal and dependant are same on the Edm side even if they are in //different order for ref constraints for Edm and store side. var childRefPropertiesCollection = childPropertiesSet.Where(it => it.SetEquals(new Set (refConstraint.ToProperties))); var parentRefPropertiesCollection = parentPropertiesSet.Where(it => it.SetEquals(new Set (refConstraint.FromProperties))); if ((childRefPropertiesCollection.Count() != 0 && parentRefPropertiesCollection.Count() != 0)) { foreach (var parentRefProperties in parentRefPropertiesCollection) { var parentIndexes = GetPropertyIndexes(parentRefProperties, refConstraint.FromProperties); foreach (var childRefProperties in childRefPropertiesCollection) { var childIndexes = GetPropertyIndexes(childRefProperties, refConstraint.ToProperties); if (childIndexes.SequenceEqual(parentIndexes)) { return true; } } } } } } return false; } //Return a set of integers that represent the indexes of first set of properties in the second set private Set GetPropertyIndexes(IEnumerable properties1, ReadOnlyMetadataCollection properties2) { var propertyIndexes = new Set (); foreach (var prop in properties1) { propertyIndexes.Add(properties2.IndexOf(prop)); } return propertyIndexes; } // requires: IsForeignKeySuperSetOfPrimaryKeyInChildTable() is false // and primaryKeys of ChildTable are not mapped in cell. cell // corresponds to an association set. parentSet is the set // corresponding to the end that we are looking at // effects: Checks if the constraint is correctly maintained in // C-space via an association set (being a subset of the // corresponding entitySet) private bool CheckConstraintWhenOnlyParentMapped(Cell cell, EntitySet parentSet, AssociationSet assocSet, AssociationEndMember endMember, QueryRewriter childRewriter, QueryRewriter parentRewriter, ConfigViewGenerator config) { ViewgenContext childContext = childRewriter.ViewgenContext; ViewgenContext parentContext = parentRewriter.ViewgenContext; CellTreeNode pNode = parentRewriter.BasicView; Debug.Assert(pNode != null); RoleBoolean endRoleBoolean = new RoleBoolean(assocSet.AssociationSetEnds[endMember.Name]); // use query in pNode as a factory to create a bool expression for the endRoleBoolean BoolExpression endCondition = pNode.RightFragmentQuery.Condition.Create(endRoleBoolean); FragmentQuery cNodeQuery = FragmentQuery.Create(pNode.RightFragmentQuery.Attributes, endCondition); FragmentQueryProcessor qp = FragmentQueryProcessor.Merge(childContext.RightFragmentQP, parentContext.RightFragmentQP); bool cImpliesP = qp.IsContainedIn(cNodeQuery, pNode.RightFragmentQuery); return cImpliesP; } // requires: IsForeignKeySuperSetOfPrimaryKeyInChildTable() is false // effects: Given that both the ChildColumns in this and the // primaryKey of ChildTable are mapped. Return true iff no error occurred private bool CheckConstraintWhenParentChildMapped(Cell cell, ErrorLog errorLog, AssociationEndMember parentEnd, ConfigViewGenerator config) { bool ok = true; // The foreign key constraint has been mapped to a // relationship. Check if the multiplicities are consistent // If all columns in the child table (corresponding to // the constraint) are nullable, the parent end can be // 0..1 or 1..1. Else if must be 1..1 if (parentEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { // Parent should at most one since we are talking // about foreign keys here string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_UpperBound_MustBeOne_2(ToUserString(), cell.CQuery.Extent.Name, parentEnd.Name); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyUpperBoundMustBeOne, message, cell, String.Empty); errorLog.AddEntry(record); ok = false; } if (MemberPath.AreAllMembersNullable(ChildColumns) == false && parentEnd.RelationshipMultiplicity != RelationshipMultiplicity.One) { // Some column in the constraint in the child table // is non-nullable and lower bound is not 1 string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_LowerBound_MustBeOne_2(ToUserString(), cell.CQuery.Extent.Name, parentEnd.Name); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyLowerBoundMustBeOne, message, cell, String.Empty); errorLog.AddEntry(record); ok = false; } if (config.IsNormalTracing && ok) { Trace.WriteLine("Foreign key mapped to relationship " + cell.CQuery.Extent.Name); } return ok; } // effects: Given the foreign key constraint, checks if the // constraint.ParentColumns are mapped to the entity set E'e keys in // C-space where E corresponds to the entity set corresponding to end // Returns true iff such a mapping exists in cell private bool CheckParentColumnsForForeignKey(Cell cell, IEnumerable cells, AssociationEndMember parentEnd, ref List | errorList) { // The child columns are mapped to some end of cell.CQuery.Extent. ParentColumns // must correspond to the EntitySet for this end AssociationSet relationSet = (AssociationSet)cell.CQuery.Extent; EntitySet endSet = MetadataHelper.GetEntitySetAtEnd(relationSet, parentEnd); // Check if the ParentColumns are mapped to endSet's keys // Find the entity set that they map to - if any EntitySet entitySet = FindEntitySetForColumnsMappedToEntityKeys(cells, ParentColumns); if (entitySet == null || endSet.Equals(entitySet) == false) { if (errorList == null) //lazily initialize only if there is an error { errorList = new List (); } // childColumns are mapped to parentEnd but ParentColumns are not mapped to the end // corresponding to the parentEnd -- this is an error string message = System.Data.Entity.Strings.ViewGen_Foreign_Key_ParentTable_NotMappedToEnd_5( ToUserString(), ChildTable.Name, cell.CQuery.Extent.Name, parentEnd.Name, ParentTable.Name, endSet.Name); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyParentTableNotMappedToEnd, message, cell, String.Empty); errorList.Add(record); return false; } return true; } #endregion #region Static Helper Methods // effects: Returns the entity set to which tableFields are mapped // and if the mapped fields correspond precisely to the entity set's // keys. Else returns null private static EntitySet FindEntitySetForColumnsMappedToEntityKeys(IEnumerable cells, IEnumerable | tableColumns) { foreach (Cell cell in cells) { CellQuery cQuery = cell.CQuery; if (cQuery.Extent is AssociationSet) { continue; } Set cSideMembers = cell.GetCSlotsForTableColumns(tableColumns); if (cSideMembers == null) { continue; } // Now check if these fields correspond to the key fields of // the entity set EntitySet entitySet = cQuery.Extent as EntitySet; // Construct a List List propertyList = new List (); foreach (EdmProperty property in entitySet.ElementType.KeyMembers) { propertyList.Add(property); } Set keyMembers = new Set (propertyList).MakeReadOnly(); if (keyMembers.SetEquals(cSideMembers)) { return entitySet; } } return null; } // effects: Returns the end to which columns are exactly mapped in the // relationship set given by cell.CQuery.Extent -- if this extent is // an entityset returns null. If the columns are not mapped in this cell to an // end exactly or columns are not projected in cell, returns null private static AssociationEndMember GetRelationEndForColumns(Cell cell, IEnumerable columns) { if (cell.CQuery.Extent is EntitySet) { return null; } AssociationSet relationSet = (AssociationSet)cell.CQuery.Extent; // Go through all the ends and see if they are mapped in this cell foreach (AssociationSetEnd relationEnd in relationSet.AssociationSetEnds) { AssociationEndMember endMember = relationEnd.CorrespondingAssociationEndMember; MemberPath prefix = new MemberPath(relationSet, endMember); // Note: primaryKey is the key for the entity set but // prefixed with the relationship's path - we are trying to // check if the entity's keys are mapped in this cell as an end ExtentKey primaryKey = ExtentKey.GetPrimaryKeyForEntityType(prefix, relationEnd.EntitySet.ElementType); // Check if this end is mapped in this cell -- we are // checking on the C-side -- we get all the indexes of the // end's keyfields List endIndexes = cell.CQuery.GetProjectedPositions(primaryKey.KeyFields); if (endIndexes != null) { // Get all the slots corresponding to the columns // But stick to the slots with in these ends since the same column might be //projected twice in different ends List columnIndexes = cell.SQuery.GetProjectedPositions(columns, endIndexes); if (columnIndexes == null) { continue; // columns are not projected with in this end } // Note that the positions need not match exactly - we have a // separate test that will do that for us: CheckForeignKeyColumnOrder if (Helpers.IsSetEqual(columnIndexes, endIndexes, EqualityComparer .Default)) { // The columns map exactly to this end -- return it return endMember; } } } return null; } // effects: Returns wrappers for extent if there are some available in the context. Else returns an empty enumeration private static List GetWrappersFromContext(ViewgenContext context, EntitySetBase extent) { List wrappers; if (context == null) { wrappers = new List (); } else { Debug.Assert(context.Extent.Equals(extent), "ViewgenContext extent and expected extent different"); wrappers = context.AllWrappersForExtent; } return wrappers; } #endregion #region Regular Helper Methods // requires: all columns in constraint.ParentColumns and // constraint.ChildColumns must have been mapped in some cell in m_cellGroup // effects: Given the foreign key constraint, checks if the // constraint.ChildColumns are mapped to the constraint.ParentColumns // in m_cellGroup in the right oder. If not, adds an error to m_errorLog and returns // false. Else returns true private bool CheckForeignKeyColumnOrder(Set cells, ErrorLog errorLog) { // Go through every cell and find the cells that are relevant to // parent and those that are relevant to child // Then for each cell pair (parent, child) make sure that the // projected foreign keys columns in C-space are aligned List | parentCells = new List | (); List | childCells = new List | (); foreach (Cell cell in cells) { if (cell.SQuery.Extent.Equals(ChildTable)) { childCells.Add(cell); } if (cell.SQuery.Extent.Equals(ParentTable)) { parentCells.Add(cell); } } // Make sure that all child cells and parent cells align on // the columns, i.e., for each DISTINCT pair C and P, get the columns // on the S-side. Then get the corresponding fields on the // C-side. The fields on the C-side should match bool foundParentCell = false; bool foundChildCell = false; foreach (Cell childCell in childCells) { List | > allChildSlotNums = GetSlotNumsForColumns(childCell, ChildColumns); if (allChildSlotNums.Count == 0) { // slots in present in S-side, ignore continue; } List
childPaths = null; List parentPaths = null; Cell errorParentCell = null; foreach (List childSlotNums in allChildSlotNums) { foundChildCell = true; // Get the fields on the C-side childPaths = new List (childSlotNums.Count); foreach (int childSlotNum in childSlotNums) { // Initial slots only have JoinTreeSlots MemberProjectedSlot childSlot = (MemberProjectedSlot)childCell.CQuery.ProjectedSlotAt(childSlotNum); Debug.Assert(childSlot != null); childPaths.Add(childSlot.MemberPath); } foreach (Cell parentCell in parentCells) { List > allParentSlotNums = GetSlotNumsForColumns(parentCell, ParentColumns); if (allParentSlotNums.Count == 0) { // * Parent and child cell are the same - we do not // need to check since we want to check the foreign // key constraint mapping across cells // * Some slots not in present in S-side, ignore continue; } foreach (List
parentSlotNums in allParentSlotNums) { foundParentCell = true; parentPaths = new List (parentSlotNums.Count); foreach (int parentSlotNum in parentSlotNums) { MemberProjectedSlot parentSlot = (MemberProjectedSlot)parentCell.CQuery.ProjectedSlotAt(parentSlotNum); Debug.Assert(parentSlot != null); parentPaths.Add(parentSlot.MemberPath); } // Make sure that the last member of each of these is the same // or the paths are essentially equivalent via referential constraints // We need to check that the last member is essentially the same because it could // be a regular scenario where aid is mapped to PersonAddress and Address - there // is no ref constraint. So when projected into C-Space, we will get Address.aid // and PersonAddress.Address.aid if (childPaths.Count == parentPaths.Count) { bool notAllPathsMatched = false; for (int i = 0; i < childPaths.Count && !notAllPathsMatched; i++) { MemberPath parentPath = parentPaths[i]; MemberPath childPath = childPaths[i]; if (!parentPath.LeafEdmMember.Equals(childPath.LeafEdmMember)) //Child path did not match { if (parentPath.IsEquivalentViaRefConstraint(childPath)) { //Specifying the referential constraint once in the C space should be enough. //This is the only way possible today. //We might be able to derive more knowledge by using boolean logic return true; } else { notAllPathsMatched = true; } } } if (!notAllPathsMatched) { return true; //all childPaths matched parentPaths } else { //If not this one, some other Parent Cell may match. errorParentCell = parentCell; } } } } //foreach parentCell } //If execution is at this point, no parent cell's end has matched (otherwise it would have returned true) Debug.Assert(childPaths != null, "child paths should be set"); Debug.Assert(parentPaths != null, "parent paths should be set"); Debug.Assert(errorParentCell != null, "errorParentCell should be set"); // using EntityRes. instead of Strings. because the generated method includes 6 instead of 9 parameters string message = EntityRes.GetString(EntityRes.ViewGen_Foreign_Key_ColumnOrder_Incorrect_8, ToUserString(), MemberPath.PropertiesToUserString(ChildColumns, false), ChildTable.Name, MemberPath.PropertiesToUserString(childPaths, false), childCell.CQuery.Extent.Name, MemberPath.PropertiesToUserString(ParentColumns, false), ParentTable.Name, MemberPath.PropertiesToUserString(parentPaths, false), errorParentCell.CQuery.Extent.Name); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyColumnOrderIncorrect, message, new Cell[] { errorParentCell, childCell }, String.Empty); errorLog.AddEntry(record); return false; } Debug.Assert(foundParentCell == true, "Some cell that mapped the parent's key must be present!"); Debug.Assert(foundChildCell == true, "Some cell that mapped the child's foreign key must be present according to the requires clause!"); return true; } private static List > GetSlotNumsForColumns(Cell cell, IEnumerable
columns) { List > slotNums = new List
>(); AssociationSet set = cell.CQuery.Extent as AssociationSet; //If it is an association set, the columns could be projected //in either end so get the slotNums from both the ends if (set != null) { foreach (AssociationSetEnd setEnd in set.AssociationSetEnds) { List
endSlots = cell.CQuery.GetAssociationEndSlots(setEnd.CorrespondingAssociationEndMember); Debug.Assert(endSlots.Count > 0); List localslotNums = cell.SQuery.GetProjectedPositions(columns, endSlots); if (localslotNums != null) { slotNums.Add(localslotNums); } } } else { List localslotNums = cell.SQuery.GetProjectedPositions(columns); if (localslotNums != null) { slotNums.Add(localslotNums); } } return slotNums; } // effects: Returns true iff the foreign keys "cover" the primary key // in the child table, e.g., covers (if k2 is the key // of the child table) private bool IsForeignKeySuperSetOfPrimaryKeyInChildTable() { bool isForeignKeySuperSet = true; foreach (EdmProperty keyMember in m_childTable.ElementType.KeyMembers) { // Look for this member in the foreign key members bool memberFound = false; foreach (MemberPath foreignKeyMember in m_childColumns) { // Getting the last member is good enough since it // effectively captures the path (we are not comparing // string names here) if (foreignKeyMember.LeafEdmMember.Equals(keyMember)) { memberFound = true; break; } } if (memberFound == false) { isForeignKeySuperSet = false; break; } } return isForeignKeySuperSet; } // effects: Returns true iff some cell in this refers to the // constraint's parent table or child table private bool IsConstraintRelevantForCells(IEnumerable cells) { // if the constraint does not deal with any cell in this group, // return false bool found = false; foreach (Cell cell in cells) { EntitySetBase table = cell.SQuery.Extent; if (table.Equals(m_parentTable) || table.Equals(m_childTable)) { found = true; break; } } return found; } #endregion #region String methods internal string ToUserString() { string childColsString = MemberPath.PropertiesToUserString(m_childColumns, false); string parentColsString = MemberPath.PropertiesToUserString(m_parentColumns, false); string result = System.Data.Entity.Strings.ViewGen_Foreign_Key_4(m_fKeySet.Name, m_childTable.Name, childColsString, m_parentTable.Name, parentColsString); return result; } internal override void ToCompactString(StringBuilder builder) { builder.Append(m_fKeySet.Name + ": "); builder.Append(ToUserString()); } #endregion } } // 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
- TransactionBehavior.cs
- TextEditorSpelling.cs
- SQLResource.cs
- ResourceDisplayNameAttribute.cs
- ScrollBar.cs
- PenThreadPool.cs
- SafeNativeMemoryHandle.cs
- filewebrequest.cs
- BaseAddressElement.cs
- Icon.cs
- PrintEvent.cs
- FileEnumerator.cs
- _HelperAsyncResults.cs
- ResumeStoryboard.cs
- CompensatableTransactionScopeActivityDesigner.cs
- Win32.cs
- InvalidDataException.cs
- HttpCapabilitiesSectionHandler.cs
- MemberRelationshipService.cs
- CapacityStreamGeometryContext.cs
- RequestCacheValidator.cs
- NameValueFileSectionHandler.cs
- Size.cs
- DateTimeFormatInfo.cs
- FormsAuthenticationModule.cs
- FormViewInsertEventArgs.cs
- SqlClientFactory.cs
- BinaryFormatter.cs
- LongAverageAggregationOperator.cs
- SqlMethodTransformer.cs
- PageThemeParser.cs
- EntityUtil.cs
- DbReferenceCollection.cs
- XamlPoint3DCollectionSerializer.cs
- tooltip.cs
- FixedSOMPageConstructor.cs
- OleDbReferenceCollection.cs
- MultiSelector.cs
- CodeExpressionCollection.cs
- AutoGeneratedField.cs
- ApplicationServiceManager.cs
- XNodeNavigator.cs
- ObjectParameter.cs
- SelectionWordBreaker.cs
- ComplexPropertyEntry.cs
- TransactionManager.cs
- _IPv6Address.cs
- ScrollEvent.cs
- OAVariantLib.cs
- TagPrefixAttribute.cs
- _BufferOffsetSize.cs
- PerformanceCounterManager.cs
- CodeTypeReferenceSerializer.cs
- DataGridViewCellStyleConverter.cs
- LeaseManager.cs
- DataGridViewComboBoxEditingControl.cs
- GenericXmlSecurityTokenAuthenticator.cs
- StringStorage.cs
- XamlInt32CollectionSerializer.cs
- SoapAttributes.cs
- CSharpCodeProvider.cs
- BasicExpandProvider.cs
- KeyboardDevice.cs
- RecognitionResult.cs
- CLSCompliantAttribute.cs
- ConfigXmlComment.cs
- InfoCardCryptoHelper.cs
- WorkflowItemPresenter.cs
- CodeGeneratorOptions.cs
- ProcessModelSection.cs
- MDIWindowDialog.cs
- prompt.cs
- DesigntimeLicenseContextSerializer.cs
- PipeStream.cs
- FindCriteria.cs
- TextRunCacheImp.cs
- DbDataSourceEnumerator.cs
- TableRowCollection.cs
- EventToken.cs
- MimeParameters.cs
- SizeConverter.cs
- AssemblyCollection.cs
- QueryStoreStatusRequest.cs
- FloatUtil.cs
- UserNamePasswordValidator.cs
- ContextProperty.cs
- ClaimTypes.cs
- GetPageCompletedEventArgs.cs
- WebEvents.cs
- mactripleDES.cs
- InternalSafeNativeMethods.cs
- CompiledQueryCacheEntry.cs
- Matrix3DValueSerializer.cs
- DataGridRow.cs
- SpellerError.cs
- HostedController.cs
- _BufferOffsetSize.cs
- LogStore.cs
- TraceListeners.cs
- FaultDescription.cs