internal override void Configure( AssociationType associationType, AssociationEndMember dependentEnd, EntityTypeConfiguration entityTypeConfiguration) { // ISSUE: object of a compiler-generated type is created // ISSUE: variable of a compiler-generated type ForeignKeyConstraintConfiguration.\u003C\u003Ec__DisplayClasse cDisplayClasse1 = new ForeignKeyConstraintConfiguration.\u003C\u003Ec__DisplayClasse(); // ISSUE: reference to a compiler-generated field cDisplayClasse1.entityTypeConfiguration = entityTypeConfiguration; if (!this._dependentProperties.Any <PropertyInfo>()) { return; } IEnumerable <PropertyInfo> propertyInfos = this._dependentProperties.AsEnumerable <PropertyInfo>(); if (!this.IsFullySpecified) { // ISSUE: reference to a compiler-generated field if (EntityTypeExtensions.GetClrType(dependentEnd.GetEntityType()) != cDisplayClasse1.entityTypeConfiguration.ClrType) { return; } // ISSUE: reference to a compiler-generated method IEnumerable <\u003C\u003Ef__AnonymousType41 <PropertyInfo, int?> > source = this._dependentProperties.Select(new Func <PropertyInfo, \u003C\u003Ef__AnonymousType41 <PropertyInfo, int?> >(cDisplayClasse1.\u003CConfigure\u003Eb__0)); if (this._dependentProperties.Count > 1 && source.Any(p => !p.ColumnOrder.HasValue)) { ReadOnlyMetadataCollection <EdmProperty> dependentKeys = dependentEnd.GetEntityType().KeyProperties; if (dependentKeys.Count != this._dependentProperties.Count || !source.All(fk => { // ISSUE: variable of a compiler-generated type ForeignKeyConstraintConfiguration.\u003C\u003Ec__DisplayClasse cDisplayClasse = cDisplayClasse1; var fk1 = fk; return(dependentKeys.Any <EdmProperty>((Func <EdmProperty, bool>)(p => p.GetClrPropertyInfo().IsSameAs(fk1.PropertyInfo)))); })) { // ISSUE: reference to a compiler-generated field throw Error.ForeignKeyAttributeConvention_OrderRequired((object)cDisplayClasse1.entityTypeConfiguration.ClrType); } propertyInfos = dependentKeys.Select <EdmProperty, PropertyInfo>((Func <EdmProperty, PropertyInfo>)(p => p.GetClrPropertyInfo())); } else { propertyInfos = source.OrderBy(p => p.ColumnOrder).Select(p => p.PropertyInfo); } } List <EdmProperty> edmPropertyList = new List <EdmProperty>(); foreach (PropertyInfo propertyInfo in propertyInfos) { EdmProperty primitiveProperty = dependentEnd.GetEntityType().GetDeclaredPrimitiveProperty(propertyInfo); if (primitiveProperty == null) { throw Error.ForeignKeyPropertyNotFound((object)propertyInfo.Name, (object)dependentEnd.GetEntityType().Name); } edmPropertyList.Add(primitiveProperty); } AssociationEndMember otherEnd = associationType.GetOtherEnd(dependentEnd); ReferentialConstraint referentialConstraint = new ReferentialConstraint((RelationshipEndMember)otherEnd, (RelationshipEndMember)dependentEnd, (IEnumerable <EdmProperty>)otherEnd.GetEntityType().KeyProperties, (IEnumerable <EdmProperty>)edmPropertyList); if (otherEnd.IsRequired()) { referentialConstraint.ToProperties.Each <EdmProperty, bool>((Func <EdmProperty, bool>)(p => p.Nullable = false)); } associationType.Constraint = referentialConstraint; }
/// <summary> /// When overriden returns <c>true</c> if <paramref name="dependentProperty"/> should be part of the foreign key. /// </summary> /// <param name="associationType"> The association type being configured. </param> /// <param name="dependentAssociationEnd"> The dependent end. </param> /// <param name="dependentProperty"> The candidate property on the dependent end. </param> /// <param name="principalEntityType"> The principal end entity type. </param> /// <param name="principalKeyProperty"> A key property on the principal end that is a candidate target for the foreign key. </param> /// <returns>true if dependentProperty should be a part of the foreign key; otherwise, false.</returns> protected abstract bool MatchDependentKeyProperty( AssociationType associationType, AssociationEndMember dependentAssociationEnd, EdmProperty dependentProperty, EntityType principalEntityType, EdmProperty principalKeyProperty);
private List <ModelBidirectionalAssociation> GetBidirectionalAssociations(EntityType entityType) { List <ModelBidirectionalAssociation> result = new List <ModelBidirectionalAssociation>(); if (entityType == null) { return(result); } foreach (NavigationProperty navigationProperty in entityType.DeclaredNavigationProperties.Where(np => Inverse(np) != null)) { StructuralType sourceType = navigationProperty.DeclaringType; if (sourceType.Name != entityType.Name) { continue; } ModelBidirectionalAssociation association = new ModelBidirectionalAssociation(); EntityType targetType = navigationProperty.ToEndMember.GetEntityType(); AssociationType associationType = navigationProperty.RelationshipType as AssociationType; ReferentialConstraint constraint = associationType?.Constraint; AssociationEndMember principalEnd = constraint?.FromRole as AssociationEndMember; EntityType principalType = principalEnd?.GetEntityType(); AssociationEndMember dependentEnd = constraint?.ToRole as AssociationEndMember; EntityType dependentType = dependentEnd?.GetEntityType(); if (principalType?.Name == sourceType.Name) { association.SourceRole = AssociationRole.Principal; association.TargetRole = AssociationRole.Dependent; } else if (principalType?.Name == targetType.Name) { association.TargetRole = AssociationRole.Principal; association.SourceRole = AssociationRole.Dependent; } else if (principalType == null && dependentType == null) { association.SourceRole = AssociationRole.NotApplicable; association.TargetRole = AssociationRole.NotApplicable; } association.SourceClassName = sourceType.Name; association.SourceClassNamespace = sourceType.NamespaceName; association.TargetClassName = targetType.Name; association.TargetClassNamespace = targetType.NamespaceName; NavigationProperty inverse = Inverse(navigationProperty); // the property in the source class (referencing the target class) association.TargetPropertyTypeName = targetType.Name; association.TargetPropertyName = navigationProperty.Name; association.TargetMultiplicity = ConvertMultiplicity(navigationProperty.ToEndMember.RelationshipMultiplicity); association.TargetSummary = navigationProperty.ToEndMember.Documentation?.Summary; association.TargetDescription = navigationProperty.ToEndMember.Documentation?.LongDescription; // the property in the target class (referencing the source class) association.SourcePropertyTypeName = navigationProperty.FromEndMember.GetEntityType().Name; association.SourcePropertyName = inverse?.Name; association.SourceMultiplicity = ConvertMultiplicity(navigationProperty.FromEndMember.RelationshipMultiplicity); association.SourceSummary = navigationProperty.FromEndMember.Documentation?.Summary; association.SourceDescription = navigationProperty.FromEndMember.Documentation?.LongDescription; // look for declared foreign keys List <EdmProperty> dependentProperties = navigationProperty.GetDependentProperties().ToList(); if (dependentProperties.Any()) { association.ForeignKey = string.Join(",", dependentProperties.Select(p => p.Name)); } log.Debug($"Found bidirectional association {association.SourceClassName}.{association.TargetPropertyName} <-> {association.TargetClassName}.{association.SourcePropertyTypeName}"); log.Debug("\n " + JsonConvert.SerializeObject(association)); result.Add(association); } return(result); }
private static Func <RelationshipManager, RelatedEnd, RelatedEnd> CreateGetRelatedEndMethod <TSource, TTarget>(AssociationEndMember sourceMember, AssociationEndMember targetMember, NavigationPropertyAccessor sourceAccessor, NavigationPropertyAccessor targetAccessor) where TSource : class where TTarget : class { Func <RelationshipManager, RelatedEnd, RelatedEnd> getRelatedEnd; // Get the appropriate method, either collection or reference depending on the target multiplicity switch (targetMember.RelationshipMultiplicity) { case RelationshipMultiplicity.ZeroOrOne: case RelationshipMultiplicity.One: { getRelatedEnd = (manager, relatedEnd) => manager.GetRelatedReference <TSource, TTarget>(sourceMember.DeclaringType.FullName, sourceMember.Name, targetMember.Name, sourceAccessor, targetAccessor, sourceMember.RelationshipMultiplicity, relatedEnd); break; } case RelationshipMultiplicity.Many: { getRelatedEnd = (manager, relatedEnd) => manager.GetRelatedCollection <TSource, TTarget>(sourceMember.DeclaringType.FullName, sourceMember.Name, targetMember.Name, sourceAccessor, targetAccessor, sourceMember.RelationshipMultiplicity, relatedEnd); break; } default: throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)targetMember.RelationshipMultiplicity); } return(getRelatedEnd); }
internal abstract void Configure( AssociationType associationType, AssociationEndMember dependentEnd, EntityTypeConfiguration entityTypeConfiguration);
// <summary> // Construct a new AssociationEnd member mapping metadata object // </summary> internal ObjectAssociationEndMapping(AssociationEndMember edmAssociationEnd, AssociationEndMember clrAssociationEnd) : base(edmAssociationEnd, clrAssociationEnd) { }
internal DirectionalRelationship(EntityKey toEntityKey, AssociationEndMember fromEnd, AssociationEndMember toEnd, AssociationSet associationSet, IEntityStateEntry stateEntry) { ToEntityKey = EntityUtil.CheckArgumentNull(toEntityKey, "toEntityKey"); FromEnd = EntityUtil.CheckArgumentNull(fromEnd, "fromEnd"); ToEnd = EntityUtil.CheckArgumentNull(toEnd, "toEnd"); AssociationSet = EntityUtil.CheckArgumentNull(associationSet, "associationSet"); StateEntry = EntityUtil.CheckArgumentNull(stateEntry, "stateEntry"); _equivalenceSetLinkedListNext = this; _hashCode = toEntityKey.GetHashCode() ^ fromEnd.GetHashCode() ^ toEnd.GetHashCode() ^ associationSet.GetHashCode(); }
internal override void Configure( AssociationType associationType, AssociationEndMember dependentEnd, EntityTypeConfiguration entityTypeConfiguration) { DebugCheck.NotNull(associationType); DebugCheck.NotNull(dependentEnd); DebugCheck.NotNull(entityTypeConfiguration); if (!_dependentProperties.Any()) { return; } var dependentPropertInfos = _dependentProperties.AsEnumerable(); if (!IsFullySpecified) { var foreignKeys = from p in _dependentProperties select new { PropertyInfo = p, entityTypeConfiguration.Property(new PropertyPath(p)).ColumnOrder }; if ((_dependentProperties.Count > 1) && foreignKeys.Any(p => !p.ColumnOrder.HasValue)) { var dependentKeys = dependentEnd.GetEntityType().KeyProperties; if ((dependentKeys.Count == _dependentProperties.Count) && foreignKeys.All(fk => dependentKeys.Any(p => p.GetClrPropertyInfo().IsSameAs(fk.PropertyInfo)))) { // The FK and PK sets are equal, we know the order dependentPropertInfos = dependentKeys.Select(p => p.GetClrPropertyInfo()); } else { throw Error.ForeignKeyAttributeConvention_OrderRequired(entityTypeConfiguration.ClrType); } } else { dependentPropertInfos = foreignKeys.OrderBy(p => p.ColumnOrder).Select(p => p.PropertyInfo); } } var dependentProperties = new List <EdmProperty>(); foreach (var dependentProperty in dependentPropertInfos) { var property = dependentEnd.GetEntityType() .GetDeclaredPrimitiveProperty(dependentProperty); if (property == null) { throw Error.ForeignKeyPropertyNotFound( dependentProperty.Name, dependentEnd.GetEntityType().Name); } dependentProperties.Add(property); } var principalEnd = associationType.GetOtherEnd(dependentEnd); var associationConstraint = new ReferentialConstraint( principalEnd, dependentEnd, principalEnd.GetEntityType().KeyProperties, dependentProperties); if (principalEnd.IsRequired()) { associationConstraint.ToProperties.Each(p => p.Nullable = false); } associationType.Constraint = associationConstraint; }
// 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 <Cell> 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 <MemberPath> primaryKeyFields = primaryKey.KeyFields; bool foundCell = false; bool foundValidParentColumnsForForeignKey = false; //we need to find only one, dont error on any one check being false List <ErrorLog.Record> 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(ToUserString()); IEnumerable <LeftCellWrapper> parentWrappers = GetWrappersFromContext(parentContext, ParentTable); IEnumerable <LeftCellWrapper> childWrappers = GetWrappersFromContext(childContext, ChildTable); Set <LeftCellWrapper> bothExtentWrappers = new Set <LeftCellWrapper>(parentWrappers); bothExtentWrappers.AddRange(childWrappers); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.ForeignKeyMissingRelationshipMapping, message, bothExtentWrappers, String.Empty); errorLog.AddEntry(record); } }
public void BuildAssociationSetMappings_builds_conceptual_association_set_mapping_for_collapsed_store_entity_sets() { #region Setting up many to many relationship in the SSpace Teacher * -- 1 TeacherStudents 1 -- * Teachers var joinStoreEntityType = EntityType.Create( "TeacherStudents", "ns.Store", DataSpace.SSpace, new[] { "JoinTeacherId", "JoinStudentId" }, new[] { CreateStoreProperty("JoinTeacherId", "int"), CreateStoreProperty("JoinStudentId", "int") }, null); var joinStoreEntitySet = EntitySet.Create("TeacherStudentsSet", "dbo", "TeacherStudentTable", null, joinStoreEntityType, null); var storeTeacherEntityType = EntityType.Create( "Teacher", "ns.Store", DataSpace.SSpace, new[] { "TeacherId" }, new[] { CreateStoreProperty("TeacherId", "int") }, null); var storeTeacherEntitySet = EntitySet.Create("TeachersSet", "dbo", "Teachers", null, storeTeacherEntityType, null); var storeStudentEntityType = EntityType.Create( "Student", "ns.Store", DataSpace.SSpace, new[] { "StudentId" }, new[] { CreateStoreProperty("StudentId", "int") }, null); var storeStudentEntitySet = EntitySet.Create("StudentSet", "dbo", "Students", null, storeStudentEntityType, null); var storeTeachersEndMember = AssociationEndMember.Create( "Teachers", storeTeacherEntityType.GetReferenceType(), RelationshipMultiplicity.Many, OperationAction.None, null); var storeTeacherStudentsfromTeachersEndMember = AssociationEndMember.Create( "TeacherStudents_fromTeachers", joinStoreEntityType.GetReferenceType(), RelationshipMultiplicity.One, OperationAction.None, null); var storeTeacherAssociationType = AssociationType.Create( "Teacher_TeacherStudentsAssociationType", "ns.Store", false, DataSpace.SSpace, storeTeachersEndMember, storeTeacherStudentsfromTeachersEndMember, new ReferentialConstraint( storeTeachersEndMember, storeTeacherStudentsfromTeachersEndMember, storeTeacherEntityType.KeyProperties, joinStoreEntityType.KeyProperties.Where(p => p.Name == "JoinTeacherId")), null); var storeTeacherAssociationSet = AssociationSet.Create( "Teacher_TeacherStudents", storeTeacherAssociationType, storeTeacherEntitySet, joinStoreEntitySet, null); var storeStudentsEndMember = AssociationEndMember.Create( "Students", storeStudentEntityType.GetReferenceType(), RelationshipMultiplicity.Many, OperationAction.None, null); var storeTeacherStudentsfromStudentsEndMember = AssociationEndMember.Create( "TeacherStudents_fromStudents", joinStoreEntityType.GetReferenceType(), RelationshipMultiplicity.One, OperationAction.None, null); var storeStudentAssociationType = AssociationType.Create( "Student_TeacherStudentsAssociationType", "ns.Store", false, DataSpace.SSpace, storeStudentsEndMember, storeTeacherStudentsfromStudentsEndMember, new ReferentialConstraint( storeStudentsEndMember, storeTeacherStudentsfromStudentsEndMember, storeStudentEntityType.KeyProperties, joinStoreEntityType.KeyProperties.Where(p => p.Name == "JoinStudentId")), null); var storeStudentAssociationSet = AssociationSet.Create( "Student_TeacherStudents", storeStudentAssociationType, storeStudentEntitySet, joinStoreEntitySet, null); var collapsedAssociationSet = new CollapsibleEntityAssociationSets(joinStoreEntitySet); collapsedAssociationSet.AssociationSets.Add(storeTeacherAssociationSet); collapsedAssociationSet.AssociationSets.Add(storeStudentAssociationSet); #endregion #region Setting up many to many relationship in the CSpace Teacher * -- * Teachers var conceptualContainer = EntityContainer.Create("ConceptualContainer", DataSpace.CSpace, null, null, null); var edmIntTypeUsage = TypeUsage.CreateDefaultTypeUsage(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)); var conceptualTeacherEntityType = EntityType.Create( "Teacher", "ns", DataSpace.CSpace, new[] { "TeacherId" }, new[] { EdmProperty.Create("TeacherId", edmIntTypeUsage) }, null); var conceptualTeacherEntitySet = EntitySet.Create("TeachersSet", null, null, null, conceptualTeacherEntityType, null); var conceptualStudentEntityType = EntityType.Create( "Student", "ns", DataSpace.CSpace, new[] { "StudentId" }, new[] { EdmProperty.Create("StudentId", edmIntTypeUsage) }, null); var conceptualStudentEntitySet = EntitySet.Create("StudentSet", "dbo", "Students", null, conceptualStudentEntityType, null); var conceptualTeachersEndMember = AssociationEndMember.Create( "TeachersEnd", conceptualTeacherEntityType.GetReferenceType(), RelationshipMultiplicity.Many, OperationAction.None, null); var conceptualStudentsEndMember = AssociationEndMember.Create( "StudentsEnd", conceptualStudentEntityType.GetReferenceType(), RelationshipMultiplicity.Many, OperationAction.None, null); var conceptualAssociationType = AssociationType.Create( "TeacherStudentAssociation", "ns.Model", false, DataSpace.CSpace, conceptualTeachersEndMember, conceptualStudentsEndMember, new ReferentialConstraint( conceptualTeachersEndMember, conceptualStudentsEndMember, conceptualTeacherEntityType.KeyProperties, conceptualStudentEntityType.KeyProperties), null); var conceptualAssociationSet = AssociationSet.Create( "TeacherStudentSet", conceptualAssociationType, conceptualTeacherEntitySet, conceptualStudentEntitySet, null); #endregion var mappingContext = new SimpleMappingContext(new EdmModel(DataSpace.SSpace), true); mappingContext.AddMapping(collapsedAssociationSet, conceptualAssociationSet); mappingContext.AddMapping(storeTeachersEndMember, conceptualTeachersEndMember); mappingContext.AddMapping(storeStudentsEndMember, conceptualStudentsEndMember); mappingContext.AddMapping( storeTeacherAssociationSet.AssociationSetEnds.ElementAt(0), conceptualAssociationSet.AssociationSetEnds.ElementAt(0)); mappingContext.AddMapping( storeStudentAssociationSet.AssociationSetEnds.ElementAt(0), conceptualAssociationSet.AssociationSetEnds.ElementAt(1)); mappingContext.AddMapping( storeStudentEntityType.KeyProperties.Single(), conceptualStudentEntityType.KeyProperties.Single()); mappingContext.AddMapping( storeTeacherEntityType.KeyProperties.Single(), conceptualTeacherEntityType.KeyProperties.Single()); var storageEntitySetMapping = new EntityContainerMapping(conceptualContainer, null, null, false, false); var associationSetMapping = DbDatabaseMappingBuilder.BuildAssociationSetMappings(storageEntitySetMapping, mappingContext) .SingleOrDefault(); Assert.NotNull(associationSetMapping); var mappingFragment = associationSetMapping.TypeMappings.SingleOrDefault(); Assert.NotNull(mappingFragment); var propertyMappings = mappingFragment.MappingFragments.Single().PropertyMappings; Assert.Equal(2, propertyMappings.Count); Assert.Same(conceptualTeachersEndMember, ((EndPropertyMapping)propertyMappings[0]).AssociationEnd); Assert.Same(conceptualStudentsEndMember, ((EndPropertyMapping)propertyMappings[1]).AssociationEnd); var scalarPropertyMapping = ((EndPropertyMapping)propertyMappings[0]).PropertyMappings.Single(); Assert.Same(conceptualTeacherEntityType.KeyMembers.Single(), scalarPropertyMapping.Property); Assert.Same( joinStoreEntityType.KeyMembers.Single(m => m.Name == "JoinTeacherId"), scalarPropertyMapping.Column); scalarPropertyMapping = ((EndPropertyMapping)propertyMappings[1]).PropertyMappings.Single(); Assert.Same(conceptualStudentEntityType.KeyMembers.Single(), scalarPropertyMapping.Property); Assert.Same( joinStoreEntityType.KeyMembers.Single(m => m.Name == "JoinStudentId"), scalarPropertyMapping.Column); }
protected virtual void Visit(AssociationEndMember associationEndMember) { Visit(associationEndMember.TypeUsage); }
public void Can_configure_ia_fk_parameters() { var modificationFunctionConfiguration = new ModificationFunctionConfiguration(); var mockPropertyInfo1 = new MockPropertyInfo(); var mockPropertyInfo2 = new MockPropertyInfo(); modificationFunctionConfiguration .Parameter(new PropertyPath(new[] { mockPropertyInfo1.Object, mockPropertyInfo2.Object }), "Foo"); var entitySet = new EntitySet(); entitySet.ChangeEntityContainerWithoutCollectionFixup(new EntityContainer("C", DataSpace.CSpace)); var property1 = new EdmProperty("P1"); property1.SetClrPropertyInfo(mockPropertyInfo1); var function = new EdmFunction("F", "N", DataSpace.SSpace); var functionParameter1 = new FunctionParameter(); var associationType = new AssociationType("A", XmlConstants.ModelNamespace_3, false, DataSpace.CSpace); var associationEndMember1 = new AssociationEndMember("AE1", new EntityType("E", "N", DataSpace.CSpace)) { RelationshipMultiplicity = RelationshipMultiplicity.Many }; var associationEndMember2 = new AssociationEndMember("AE2", new EntityType("E", "N", DataSpace.CSpace)) { RelationshipMultiplicity = RelationshipMultiplicity.One }; associationEndMember2.SetClrPropertyInfo(mockPropertyInfo1); associationType.SourceEnd = associationEndMember1; associationType.TargetEnd = associationEndMember2; var associationSet = new AssociationSet("AS", associationType); associationSet.AddAssociationSetEnd( new AssociationSetEnd(entitySet, associationSet, associationEndMember2)); modificationFunctionConfiguration.Configure( new StorageModificationFunctionMapping( entitySet, new EntityType("E", "N", DataSpace.CSpace), function, new[] { new StorageModificationFunctionParameterBinding( functionParameter1, new StorageModificationFunctionMemberPath( new EdmMember[] { property1, associationEndMember2 }, associationSet), false) }, null, null), ProviderRegistry.Sql2008_ProviderManifest); Assert.Equal("Foo", functionParameter1.Name); }
public void Can_configure_function_name_and_parameters() { var modificationFunctionConfiguration = new ModificationFunctionConfiguration(); modificationFunctionConfiguration.HasName("Foo", "Bar"); var mockPropertyInfo1 = new MockPropertyInfo(); var mockPropertyInfo2 = new MockPropertyInfo(); modificationFunctionConfiguration .Parameter(new PropertyPath(mockPropertyInfo1), "Foo"); modificationFunctionConfiguration .Parameter(new PropertyPath(new[] { mockPropertyInfo1.Object, mockPropertyInfo2.Object }), "Bar"); var entitySet = new EntitySet(); entitySet.ChangeEntityContainerWithoutCollectionFixup(new EntityContainer("C", DataSpace.CSpace)); var property1 = new EdmProperty("P1"); property1.SetClrPropertyInfo(mockPropertyInfo1); var property2 = new EdmProperty("P1"); property2.SetClrPropertyInfo(mockPropertyInfo2); var functionParameter1 = new FunctionParameter( "P1", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String)), ParameterMode.In); var functionParameter2 = new FunctionParameter( "P2", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String)), ParameterMode.In); var functionParameter3 = new FunctionParameter( "Foo", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String)), ParameterMode.In); var function = new EdmFunction( "F", "N", DataSpace.SSpace, new EdmFunctionPayload { Parameters = new[] { functionParameter1, functionParameter2, functionParameter3 } }); var associationType = new AssociationType("A", XmlConstants.ModelNamespace_3, false, DataSpace.CSpace); var associationEndMember1 = new AssociationEndMember("AE1", new EntityType("E", "N", DataSpace.CSpace)) { RelationshipMultiplicity = RelationshipMultiplicity.Many }; var associationEndMember2 = new AssociationEndMember("AE2", new EntityType("E", "N", DataSpace.CSpace)) { RelationshipMultiplicity = RelationshipMultiplicity.Many }; associationType.SourceEnd = associationEndMember1; associationType.TargetEnd = associationEndMember2; var associationSet = new AssociationSet("AS", associationType); associationSet.AddAssociationSetEnd( new AssociationSetEnd(entitySet, associationSet, associationEndMember2)); modificationFunctionConfiguration.Configure( new StorageModificationFunctionMapping( entitySet, new EntityType("E", "N", DataSpace.CSpace), function, new[] { new StorageModificationFunctionParameterBinding( functionParameter1, new StorageModificationFunctionMemberPath( new EdmMember[] { property1, associationEndMember2 }, associationSet), false), new StorageModificationFunctionParameterBinding( functionParameter2, new StorageModificationFunctionMemberPath( new[] { property1, property2 }, null), false) }, null, null), ProviderRegistry.Sql2008_ProviderManifest); Assert.Equal("Foo", function.StoreFunctionNameAttribute); Assert.Equal("Bar", function.Schema); Assert.Equal("Foo", functionParameter1.Name); Assert.Equal("Bar", functionParameter2.Name); Assert.Equal("Foo1", functionParameter3.Name); }
internal static EntityType GetEntityType(AssociationEndMember end) { EntityType entityType = (EntityType)((RefType)end.TypeUsage.EdmType).ElementType; return(entityType); }
/// <summary> /// Initializes a new instance of AssocationSetEnd /// </summary> /// <param name="entitySet">Entity set that this end refers to</param> /// <param name="parentSet">The association set which this belongs to</param> /// <param name="endMember">The end member of the association set which this is an instance of</param> /// <exception cref="System.ArgumentNullException">Thrown if either the role,entitySet, parentSet or endMember arguments are null </exception> internal AssociationSetEnd(EntitySet entitySet, AssociationSet parentSet, AssociationEndMember endMember) { _entitySet = EntityUtil.GenericCheckArgumentNull(entitySet, "entitySet"); _parentSet = EntityUtil.GenericCheckArgumentNull(parentSet, "parentSet"); _endMember = EntityUtil.GenericCheckArgumentNull(endMember, "endMember"); }
// 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); }
// <summary> // Determines whether the specified { from, to } relationship end pairing represents a navigation that is // valid for a relationship span sourced by an instance of the specified entity type. // </summary> // <param name="compareType"> The Entity type which valid 'from' ends must reference (or a supertype of that Entity type) </param> // <param name="associationType"> The Association type to consider. </param> // <param name="fromEnd"> The candidate 'from' end, which will be checked based on the Entity type it references </param> // <param name="toEnd"> The candidate 'to' end, which will be checked base on the upper bound of its multiplicity </param> // <returns> // <c>True</c> if the end pairing represents a valid navigation from an instance of the specified entity type to an association end with a multiplicity upper bound of at most 1; otherwise <c>false</c> // </returns> private static bool IsValidRelationshipSpan( EntityType compareType, AssociationType associationType, AssociationEndMember fromEnd, AssociationEndMember toEnd) { // Only a relationship end with a multiplicity of AT MOST one may be // considered as the 'to' end, so that the cardinality of the result // of the relationship span has an upper bound of 1. // Therefore ends with RelationshipMultiplicity of EITHER One OR ZeroOrOne // are the only ends that should be considered as target ends. // Note that a relationship span can be sourced by an Entity that is of the same type // as the Entity type referenced by the 'from' end OR any type in the same branch of // the type hierarchy. // // For example, in the following hierarchy: // // A (*<-->?) AOwner // |_B (*<-->1) BOwner // |_A1 (*<-->?) A1Owner // |_A2 // |_A3_1 (1<-->?) A3_1Owner // |_A3_2 (*<-->1) A3_2Owner // // An instance of 'A' would need ALL the 'AOwner', 'BOwner', 'A1Owner', 'A3_1Owner' and 'A3_2Owner' ends // spanned in because an instance of 'A' could actually be an instance of A, B, A1, A2, A3_1 or A3_2. // An instance of 'B' would only need 'AOwner' and 'BOwner' spanned in. // An instance of A2 would need 'AOwner', 'A1Owner', 'A3_1Owner' and 'A3_2Owner' spanned in. // An instance of A3_1 would only need 'AOwner', 'A1Owner' and 'A3_1Owner' spanned in. // // In general, the rule for relationship span is: // - 'To' end cardinality AT MOST one // AND // - Referenced Entity type of 'From' end is equal to instance Entity type // OR // - Referenced Entity type of 'From' end is a supertype of instance Entity type // OR // - Referenced Entity type of 'From' end is a subtype of instance Entity type // (this follows from the fact that an instance of 'A' may be an instance of any of its derived types. // Navigation for a subtype relationship will return null if the Entity instance navigation source // is not actually of the required subtype). // if (!associationType.IsForeignKey && (RelationshipMultiplicity.One == toEnd.RelationshipMultiplicity || RelationshipMultiplicity.ZeroOrOne == toEnd.RelationshipMultiplicity)) { var fromEntityType = (EntityType)((RefType)fromEnd.TypeUsage.EdmType).ElementType; return(EntityTypeEquals(compareType, fromEntityType) || TypeSemantics.IsSubTypeOf(compareType, fromEntityType) || TypeSemantics.IsSubTypeOf(fromEntityType, compareType)); } return(false); }
// 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 <Cell> cells, AssociationEndMember parentEnd, ref List <ErrorLog.Record> 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 <ErrorLog.Record>(); } // 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( 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); }
/// <summary> /// Given an AssociationEndMember of this Association, this method will return the other end participating /// in the association /// </summary> /// <param name="association">The Association.</param> /// <param name="end">An AssociationEndMember.</param> /// <returns>The other end.</returns> public static AssociationEndMember GetOtherEnd(this AssociationType association, AssociationEndMember end) { if (end != null) { return(end == association.GetEnd1() ? association.GetEnd2() : association.GetEnd1()); } return(null); }
internal static bool DoesEndKeySubsumeAssociationSetKey(AssociationSet assocSet, AssociationEndMember thisEnd, HashSet <Pair <EdmMember, EntityType> > associationkeys) { AssociationType assocType = assocSet.ElementType; EntityType thisEndsEntityType = (EntityType)((RefType)thisEnd.TypeUsage.EdmType).ElementType; HashSet <Pair <EdmMember, EntityType> > thisEndKeys = new HashSet <Pair <EdmMember, EntityType> >( thisEndsEntityType.KeyMembers.Select(edmMember => new Pair <EdmMember, EntityType>(edmMember, thisEndsEntityType))); foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints) { IEnumerable <EdmMember> otherEndProperties; EntityType otherEndType; if (thisEnd.Equals((AssociationEndMember)constraint.ToRole)) { otherEndProperties = Helpers.AsSuperTypeList <EdmProperty, EdmMember>(constraint.FromProperties); otherEndType = (EntityType)((RefType)((AssociationEndMember)constraint.FromRole).TypeUsage.EdmType).ElementType; } else if (thisEnd.Equals((AssociationEndMember)constraint.FromRole)) { otherEndProperties = Helpers.AsSuperTypeList <EdmProperty, EdmMember>(constraint.ToProperties); otherEndType = (EntityType)((RefType)((AssociationEndMember)constraint.ToRole).TypeUsage.EdmType).ElementType; } else { //this end not part of the referential constraint continue; } //Essentially ref constraints is an equality condition, so remove redundant members from entity set key foreach (EdmMember member in otherEndProperties) { associationkeys.Remove(new Pair <EdmMember, EntityType>(member, otherEndType)); } } //Now that all redundant members have been removed, is thisEnd the key of the entity set? return(associationkeys.IsSubsetOf(thisEndKeys)); }
private static Func <RelationshipManager, RelatedEnd, RelatedEnd> CreateGetRelatedEndMethod(AssociationEndMember sourceMember, AssociationEndMember targetMember) { Debug.Assert(sourceMember.DeclaringType == targetMember.DeclaringType, "Source and Target members must be in the same DeclaringType"); EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceMember); EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetMember); NavigationPropertyAccessor sourceAccessor = MetadataHelper.GetNavigationPropertyAccessor(targetEntityType, targetMember, sourceMember); NavigationPropertyAccessor targetAccessor = MetadataHelper.GetNavigationPropertyAccessor(sourceEntityType, sourceMember, targetMember); MethodInfo genericCreateRelatedEndMethod = typeof(LightweightCodeGenerator).GetMethod("CreateGetRelatedEndMethod", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(AssociationEndMember), typeof(AssociationEndMember), typeof(NavigationPropertyAccessor), typeof(NavigationPropertyAccessor) }, null); Debug.Assert(genericCreateRelatedEndMethod != null, "Could not find method LightweightCodeGenerator.CreateGetRelatedEndMethod"); MethodInfo createRelatedEndMethod = genericCreateRelatedEndMethod.MakeGenericMethod(sourceEntityType.ClrType, targetEntityType.ClrType); object getRelatedEndDelegate = createRelatedEndMethod.Invoke(null, new object[] { sourceMember, targetMember, sourceAccessor, targetAccessor }); return((Func <RelationshipManager, RelatedEnd, RelatedEnd>)getRelatedEndDelegate); }
internal static NavigationPropertyAccessor GetNavigationPropertyAccessor(EntityType sourceEntityType, AssociationEndMember sourceMember, AssociationEndMember targetMember) { Debug.Assert(sourceEntityType.DataSpace == DataSpace.OSpace && sourceEntityType.ClrType != null, "sourceEntityType must contain an ospace type"); return(GetNavigationPropertyAccessor(sourceEntityType, sourceMember.DeclaringType.FullName, sourceMember.Name, targetMember.Name)); }
/// <summary> /// Gets the related end instance for the source AssociationEndMember by creating a DynamicMethod to /// call GetRelatedCollection or GetRelatedReference /// </summary> internal static RelatedEnd GetRelatedEnd(RelationshipManager sourceRelationshipManager, AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd) { Func <RelationshipManager, RelatedEnd, RelatedEnd> getRelatedEnd = sourceMember.GetRelatedEnd; if (null == getRelatedEnd) { getRelatedEnd = CreateGetRelatedEndMethod(sourceMember, targetMember); sourceMember.GetRelatedEnd = getRelatedEnd; } Debug.Assert(null != getRelatedEnd, "null getRelatedEnd"); return(getRelatedEnd(sourceRelationshipManager, existingRelatedEnd)); }
private void logForeignKeyChange(ObjectStateEntry entry, AssociationEndMember localEnd, AssociationEndMember foreignEnd) { // These "keys" represent in-memory unique references // to the objects at the ends of these associations. // In the case of new objects, these won't contain the primary key that has just // been generated, but we can still use it below to get to the real object in-memory // and pull the id out of that. var key = getEndEntityKey(entry, localEnd); // Get the object identified by the local key object entity = context.GetObjectByKey(key); if (!filter.ShouldLog(entity.GetType())) { return; } // The property on the "local" object that navigates to the "foreign" object var property = getProperty(entry, localEnd, foreignEnd, key); // We can control which directions of relationships we are interested in logging // by which navigation properties we keep in the model if (property == null || !filter.ShouldLog(property)) { return; } // Generate the change Func <object> value = getForeignValue(entry, entity, foreignEnd, property.Name); recorder.Record(entity, context.GetReferenceForObject(entity), property.Name, value, entry.State, null /* TODO: Fabricio retirar */); }
public void Can_generate_function_mappings_for_many_to_many_association_set_mapping() { var databaseMapping = new DbDatabaseMapping() .Initialize(new EdmModel(DataSpace.CSpace), new EdmModel(DataSpace.SSpace)); var entityType1 = databaseMapping.Model.AddEntityType("E1"); entityType1.Annotations.SetClrType(typeof(string)); databaseMapping.Model.AddEntitySet("E1Set", entityType1); var entityType2 = databaseMapping.Model.AddEntityType("E2"); entityType2.Annotations.SetClrType(typeof(string)); databaseMapping.Model.AddEntitySet("E2Set", entityType2); var entityTypeConfiguration = new EntityTypeConfiguration(typeof(object)); entityTypeConfiguration.MapToStoredProcedures(); entityType1.SetConfiguration(entityTypeConfiguration); entityType2.SetConfiguration(entityTypeConfiguration); var associationSet = databaseMapping.Model.AddAssociationSet( "M2MSet", databaseMapping.Model.AddAssociationType( "M2M", entityType1, RelationshipMultiplicity.Many, entityType2, RelationshipMultiplicity.Many)); var entitySet = new EntitySet("ES", "S", null, null, new EntityType("E", "N", DataSpace.CSpace)); var associationEndMember1 = new AssociationEndMember("Source", new EntityType("E", "N", DataSpace.CSpace)); associationSet.AddAssociationSetEnd(new AssociationSetEnd(entitySet, associationSet, associationEndMember1)); var associationEndMember2 = new AssociationEndMember("Target", new EntityType("E", "N", DataSpace.CSpace)); associationSet.AddAssociationSetEnd(new AssociationSetEnd(entitySet, associationSet, associationEndMember2)); var associationSetMapping = new StorageAssociationSetMapping( associationSet, entitySet) { SourceEndMapping = new StorageEndPropertyMapping() { EndMember = associationEndMember1 }, TargetEndMapping = new StorageEndPropertyMapping() { EndMember = associationEndMember2 } }; associationSetMapping.SourceEndMapping .AddProperty(new StorageScalarPropertyMapping(new EdmProperty("PK"), new EdmProperty("FooId"))); associationSetMapping.TargetEndMapping .AddProperty(new StorageScalarPropertyMapping(new EdmProperty("PK"), new EdmProperty("BarId"))); var functionMappingGenerator = new ModificationFunctionMappingGenerator(ProviderRegistry.Sql2008_ProviderManifest); functionMappingGenerator.Generate(associationSetMapping, databaseMapping); var modificationFunctionMapping = associationSetMapping.ModificationFunctionMapping; Assert.NotNull(modificationFunctionMapping); var functionMapping = modificationFunctionMapping.InsertFunctionMapping; Assert.NotNull(functionMapping); Assert.Equal(2, functionMapping.ParameterBindings.Count); Assert.Null(functionMapping.ResultBindings); var function = functionMapping.Function; Assert.NotNull(function); Assert.Equal("E1E2_Insert", function.Name); Assert.Equal(2, function.Parameters.Count); functionMapping = modificationFunctionMapping.DeleteFunctionMapping; Assert.NotNull(functionMapping); Assert.Equal(2, functionMapping.ParameterBindings.Count); Assert.Null(functionMapping.ResultBindings); function = modificationFunctionMapping.DeleteFunctionMapping.Function; Assert.NotNull(function); Assert.Equal("E1E2_Delete", function.Name); Assert.Equal(2, function.Parameters.Count); }
// <summary> // Call to ensure a target entities key is added into the state manager // properly // </summary> public IEntityWrapper HandleRelationshipSpan( IEntityWrapper wrappedEntity, EntityKey targetKey, AssociationEndMember targetMember) { if (null == wrappedEntity.Entity) { return(wrappedEntity); } DebugCheck.NotNull(targetMember); Debug.Assert( targetMember.RelationshipMultiplicity == RelationshipMultiplicity.One || targetMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne); var sourceKey = wrappedEntity.EntityKey; var sourceMember = MetadataHelper.GetOtherAssociationEnd(targetMember); CheckClearedEntryOnSpan(targetKey, wrappedEntity, sourceKey, targetMember); if (null != (object)targetKey) { EntitySet targetEntitySet; var associationSet = Context.MetadataWorkspace.MetadataOptimization.FindCSpaceAssociationSet( (AssociationType)targetMember.DeclaringType, targetMember.Name, targetKey.EntitySetName, targetKey.EntityContainerName, out targetEntitySet); Debug.Assert(associationSet != null, "associationSet should not be null"); var manager = Context.ObjectStateManager; EntityState newEntryState; // If there is an existing relationship entry, update it based on its current state and the MergeOption, otherwise add a new one if ( !ObjectStateManager.TryUpdateExistingRelationships( Context, MergeOption, associationSet, sourceMember, sourceKey, wrappedEntity, targetMember, targetKey, /*setIsLoaded*/ true, out newEntryState)) { // Try to find a state entry for the target key var targetEntry = manager.GetOrAddKeyEntry(targetKey, targetEntitySet); // For 1-1 relationships we have to take care of the relationships of targetEntity var needNewRelationship = true; switch (sourceMember.RelationshipMultiplicity) { case RelationshipMultiplicity.ZeroOrOne: case RelationshipMultiplicity.One: // devnote: targetEntry can be a key entry (targetEntry.Entity == null), // but it that case this parameter won't be used in TryUpdateExistingRelationships needNewRelationship = !ObjectStateManager.TryUpdateExistingRelationships( Context, MergeOption, associationSet, targetMember, targetKey, targetEntry.WrappedEntity, sourceMember, sourceKey, setIsLoaded: true, newEntryState: out newEntryState); // It is possible that as part of removing existing relationships, the key entry was deleted // If that is the case, recreate the key entry if (targetEntry.State == EntityState.Detached) { targetEntry = manager.AddKeyEntry(targetKey, targetEntitySet); } break; case RelationshipMultiplicity.Many: // we always need a new relationship with Many-To-Many, if there was no exact match between these two entities, so do nothing break; default: Debug.Assert(false, "Unexpected sourceMember.RelationshipMultiplicity"); break; } if (needNewRelationship) { // If the target entry is a key entry, then we need to add a relation // between the source and target entries // If we are in a state where we just need to add a new Deleted relation, we // only need to do that and not touch the related ends // If the target entry is a full entity entry, then we need to add // the target entity to the source collection or reference if (targetEntry.IsKeyEntry || newEntryState == EntityState.Deleted) { // Add a relationship between the source entity and the target key entry var wrapper = new RelationshipWrapper( associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey); manager.AddNewRelation(wrapper, newEntryState); } else { Debug.Assert(!targetEntry.IsRelationship, "how IsRelationship?"); if (targetEntry.State != EntityState.Deleted) { // The entry contains an entity, do collection or reference fixup // This will also try to create a new relationship entry or will revert the delete on an existing deleted relationship ObjectStateManager.AddEntityToCollectionOrReference( MergeOption, wrappedEntity, sourceMember, targetEntry.WrappedEntity, targetMember, setIsLoaded: true, relationshipAlreadyExists: false, inKeyEntryPromotion: false); } else { // if the target entry is deleted, then the materializer needs to create a deleted relationship // between the entity and the target entry so that if the entity is deleted, the update // pipeline can find the relationship (even though it is deleted) var wrapper = new RelationshipWrapper( associationSet, sourceMember.Name, sourceKey, targetMember.Name, targetKey); manager.AddNewRelation(wrapper, EntityState.Deleted); } } } } } else { RelatedEnd relatedEnd; if (TryGetRelatedEnd( wrappedEntity, (AssociationType)targetMember.DeclaringType, sourceMember.Name, targetMember.Name, out relatedEnd)) { SetIsLoadedForSpan(relatedEnd, false); } } // else there is nothing else for us to do, the relationship has been handled already return(wrappedEntity); }
internal void CreateAssociationConstraints(EntitySetBase extent, MemberDomainMap domainMap, EdmItemCollection edmItemCollection) { AssociationSet assocSet = extent as AssociationSet; if (assocSet != null) { BoolExpression assocSetExpr = BoolExpression.CreateLiteral(new RoleBoolean(assocSet), domainMap); //Set of Keys for this Association Set //need to key on EdmMember and EdmType because A, B subtype of C, can have the same id (EdmMember) that is defined in C. HashSet <Pair <EdmMember, EntityType> > associationkeys = new HashSet <Pair <EdmMember, EntityType> >(); //foreach end, add each Key foreach (var endMember in assocSet.ElementType.AssociationEndMembers) { EntityType type = (EntityType)((RefType)endMember.TypeUsage.EdmType).ElementType; type.KeyMembers.All(member => associationkeys.Add(new Pair <EdmMember, EntityType>(member, type)) || true /* prevent early termination */); } foreach (AssociationSetEnd end in assocSet.AssociationSetEnds) { // construct type condition HashSet <EdmType> derivedTypes = new HashSet <EdmType>(); derivedTypes.UnionWith(MetadataHelper.GetTypeAndSubtypesOf(end.CorrespondingAssociationEndMember.TypeUsage.EdmType, edmItemCollection, false)); BoolExpression typeCondition = CreateIsOfTypeCondition(new MemberPath(end.EntitySet), derivedTypes, domainMap); BoolExpression inRoleExpression = BoolExpression.CreateLiteral(new RoleBoolean(end), domainMap); BoolExpression inSetExpression = BoolExpression.CreateAnd( BoolExpression.CreateLiteral(new RoleBoolean(end.EntitySet), domainMap), typeCondition); // InRole -> (InSet AND type(Set)=T) AddImplication(inRoleExpression.Tree, inSetExpression.Tree); if (MetadataHelper.IsEveryOtherEndAtLeastOne(assocSet, end.CorrespondingAssociationEndMember)) { AddImplication(inSetExpression.Tree, inRoleExpression.Tree); } // Add equivalence between association set an End/Role if necessary. // Equivalence is added when a given association end's keys subsumes keys for // all the other association end. // For example: We have Entity Sets A[id1], B[id2, id3] and an association A_B between them. // Ref Constraint A.id1 = B.id2 // In this case, the Association Set has Key <id1, id2, id3> // id1 alone can not identify a unique tuple in the Association Set, but <id2, id3> can. // Therefore we add a constraint: InSet(B) <=> InEnd(A_B.B) if (MetadataHelper.DoesEndKeySubsumeAssociationSetKey(assocSet, end.CorrespondingAssociationEndMember, associationkeys)) { AddEquivalence(inRoleExpression.Tree, assocSetExpr.Tree); } } // add rules for referential constraints (borrowed from LeftCellWrapper.cs) AssociationType assocType = assocSet.ElementType; foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints) { AssociationEndMember toEndMember = (AssociationEndMember)constraint.ToRole; EntitySet toEntitySet = MetadataHelper.GetEntitySetAtEnd(assocSet, toEndMember); // Check if the keys of the entitySet's are equal to what is specified in the constraint // How annoying that KeyMembers returns EdmMember and not EdmProperty IEnumerable <EdmMember> toProperties = Helpers.AsSuperTypeList <EdmProperty, EdmMember>(constraint.ToProperties); if (Helpers.IsSetEqual(toProperties, toEntitySet.ElementType.KeyMembers, EqualityComparer <EdmMember> .Default)) { // Now check that the FromEnd is 1..1 (only then will all the Addresses be present in the assoc set) if (constraint.FromRole.RelationshipMultiplicity.Equals(RelationshipMultiplicity.One)) { // Make sure that the ToEnd is not 0..* because then the schema is broken Debug.Assert(constraint.ToRole.RelationshipMultiplicity.Equals(RelationshipMultiplicity.Many) == false); // Equate the ends BoolExpression inRoleExpression1 = BoolExpression.CreateLiteral(new RoleBoolean(assocSet.AssociationSetEnds[0]), domainMap); BoolExpression inRoleExpression2 = BoolExpression.CreateLiteral(new RoleBoolean(assocSet.AssociationSetEnds[1]), domainMap); AddEquivalence(inRoleExpression1.Tree, inRoleExpression2.Tree); } } } } }
private bool TryGetRelatedEnd( IEntityWrapper wrappedEntity, AssociationType associationType, string sourceEndName, string targetEndName, out RelatedEnd relatedEnd) { Debug.Assert(associationType.DataSpace == DataSpace.CSpace); // Get the OSpace AssociationType var oSpaceAssociation = Workspace.MetadataOptimization.GetOSpaceAssociationType(associationType, () => Workspace.GetItemCollection(DataSpace.OSpace).GetItem <AssociationType>(associationType.FullName)); AssociationEndMember sourceEnd = null; AssociationEndMember targetEnd = null; foreach (var end in oSpaceAssociation.AssociationEndMembers) { if (end.Name == sourceEndName) { sourceEnd = end; } else if (end.Name == targetEndName) { targetEnd = end; } } if (sourceEnd != null && targetEnd != null) { var createRelatedEnd = false; if (wrappedEntity.EntityKey == null) { // Free-floating entity--key is null, so don't have EntitySet for validation, so always create RelatedEnd createRelatedEnd = true; } else { // It is possible, because of MEST, that we're trying to load a relationship that is valid for this EntityType // in metadata, but is not valid in this case because the specific entity is part of an EntitySet that is not // mapped in any AssociationSet for this association type. // The metadata structure makes checking for this somewhat time consuming because of the loop required. // Because the whole reason for this method is perf, we try to reduce the // impact of this check by caching positive hits in a HashSet so we don't have to do this for // every entity in a query. (We could also cache misses, but since these only happen in MEST, which // is not common, we decided not to slow down the normal non-MEST case anymore by doing this.) EntitySet entitySet; var associationSet = Workspace.MetadataOptimization.FindCSpaceAssociationSet(associationType, sourceEndName, wrappedEntity.EntityKey.EntitySetName, wrappedEntity.EntityKey.EntityContainerName, out entitySet); if (associationSet != null) { createRelatedEnd = true; } } if (createRelatedEnd) { relatedEnd = DelegateFactory.GetRelatedEnd(wrappedEntity.RelationshipManager, sourceEnd, targetEnd, null); return(true); } } relatedEnd = null; return(false); }
public void WriteAssociationSetMapping_should_write_modification_function_mapping() { var fixture = new Fixture(); var entityType = new EntityType("E", "N", DataSpace.CSpace); var entitySet = new EntitySet("ES", "S", null, null, entityType); new EntityContainer("EC", DataSpace.SSpace).AddEntitySetBase(entitySet); var associationSet = new AssociationSet("AS", new AssociationType("A", XmlConstants.ModelNamespace_3, false, DataSpace.CSpace)); var associationEndMember1 = new AssociationEndMember("Source", new EntityType("E", "N", DataSpace.CSpace)); associationSet.AddAssociationSetEnd(new AssociationSetEnd(entitySet, associationSet, associationEndMember1)); var associationEndMember2 = new AssociationEndMember("Target", new EntityType("E", "N", DataSpace.CSpace)); associationSet.AddAssociationSetEnd(new AssociationSetEnd(entitySet, associationSet, associationEndMember2)); var storageModificationFunctionMapping = new StorageModificationFunctionMapping( associationSet, associationSet.ElementType, new EdmFunction("F", "N", DataSpace.SSpace, new EdmFunctionPayload()), new[] { new StorageModificationFunctionParameterBinding( new FunctionParameter( "P", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)), ParameterMode.In), new StorageModificationFunctionMemberPath( new EdmMember[] { new EdmProperty("K"), associationEndMember1 }, associationSet), true), new StorageModificationFunctionParameterBinding( new FunctionParameter( "P", TypeUsage.Create(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32)), ParameterMode.In), new StorageModificationFunctionMemberPath( new EdmMember[] { new EdmProperty("K"), associationEndMember2 }, associationSet), false) }, null, null); var associationSetMapping = new StorageAssociationSetMapping( associationSet, entitySet) { SourceEndMapping = new StorageEndPropertyMapping { EndMember = associationEndMember1 }, TargetEndMapping = new StorageEndPropertyMapping { EndMember = associationEndMember2 }, ModificationFunctionMapping = new StorageAssociationSetModificationFunctionMapping( associationSet, storageModificationFunctionMapping, storageModificationFunctionMapping) }; fixture.Writer.WriteAssociationSetMappingElement(associationSetMapping); Assert.Equal( @"<AssociationSetMapping Name=""AS"" TypeName="".A"" StoreEntitySet=""E""> <EndProperty Name=""Source"" /> <EndProperty Name=""Target"" /> <ModificationFunctionMapping> <InsertFunction FunctionName=""N.F""> <EndProperty Name=""Source""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Current"" /> </EndProperty> <EndProperty Name=""Target""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Original"" /> </EndProperty> </InsertFunction> <DeleteFunction FunctionName=""N.F""> <EndProperty Name=""Source""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Current"" /> </EndProperty> <EndProperty Name=""Target""> <ScalarProperty Name=""K"" ParameterName=""P"" Version=""Original"" /> </EndProperty> </DeleteFunction> </ModificationFunctionMapping> </AssociationSetMapping>", fixture.ToString()); }
internal void Delete(bool doFixup) { this.ValidateState(); if (doFixup) { if (this.State == EntityState.Deleted) { return; } EntityEntry entityEntry1 = this._cache.GetEntityEntry((EntityKey)this.GetCurrentRelationValue(0)); IEntityWrapper wrappedEntity1 = entityEntry1.WrappedEntity; EntityEntry entityEntry2 = this._cache.GetEntityEntry((EntityKey)this.GetCurrentRelationValue(1)); IEntityWrapper wrappedEntity2 = entityEntry2.WrappedEntity; if (wrappedEntity1.Entity != null && wrappedEntity2.Entity != null) { string name = this._relationshipWrapper.AssociationEndMembers[1].Name; string fullName = ((AssociationSet)this._entitySet).ElementType.FullName; wrappedEntity1.RelationshipManager.RemoveEntity(name, fullName, wrappedEntity2); } else { EntityKey entityKey; RelationshipManager relationshipManager; if (wrappedEntity1.Entity == null) { entityKey = entityEntry1.EntityKey; relationshipManager = wrappedEntity2.RelationshipManager; } else { entityKey = entityEntry2.EntityKey; relationshipManager = wrappedEntity1.RelationshipManager; } AssociationEndMember associationEndMember = this.RelationshipWrapper.GetAssociationEndMember(entityKey); ((EntityReference)relationshipManager.GetRelatedEndInternal(associationEndMember.DeclaringType.FullName, associationEndMember.Name)).DetachedEntityKey = (EntityKey)null; if (this.State == EntityState.Added) { this.DeleteUnnecessaryKeyEntries(); this.DetachRelationshipEntry(); } else { this._cache.ChangeState(this, this.State, EntityState.Deleted); this.State = EntityState.Deleted; } } } else { switch (this.State) { case EntityState.Unchanged: this._cache.ChangeState(this, EntityState.Unchanged, EntityState.Deleted); this.State = EntityState.Deleted; break; case EntityState.Added: this.DeleteUnnecessaryKeyEntries(); this.DetachRelationshipEntry(); break; } } }
// effects: Returns the entity set at the end corresponding to endMember internal static EntitySet GetEntitySetAtEnd( AssociationSet associationSet, AssociationEndMember endMember) { return(associationSet.AssociationSetEnds[endMember.Name].EntitySet); }
/// <summary> /// Populate the ResourceAssociationTypeEnd from the AssociationEndMember instance. /// </summary> /// <param name="end">Instance of AssociationEndMember.</param> /// <param name="resourceType">ResourceType instance which the end refers to.</param> /// <param name="resourceProperty">ResourceProperty instance.</param> /// <returns>An instance of ResourceAssociationTypeEnd.</returns> private static ResourceAssociationTypeEnd PopulateResourceAssociationTypeEnd(AssociationEndMember end, ResourceType resourceType, ResourceProperty resourceProperty) { ResourceAssociationTypeEnd resourceAssociationTypeEnd = new ResourceAssociationTypeEnd(end.Name, resourceType, resourceProperty, ObjectContextServiceProvider.GetMultiplicity(end.RelationshipMultiplicity), (EdmOnDeleteAction)end.DeleteBehavior); ObjectContextServiceProvider.PopulateAnnotations(end.MetadataProperties, resourceAssociationTypeEnd.AddCustomAnnotation); return resourceAssociationTypeEnd; }